mtd/drivers/mtd/nand nand.c,1.68,1.69

gleixner at infradead.org gleixner at infradead.org
Sun Mar 28 19:39:33 EST 2004


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

Modified Files:
	nand.c 
Log Message:
Fix multi chip support

Index: nand.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/nand/nand.c,v
retrieving revision 1.68
retrieving revision 1.69
diff -u -r1.68 -r1.69
--- nand.c	28 Mar 2004 23:34:07 -0000	1.68
+++ nand.c	29 Mar 2004 00:39:31 -0000	1.69
@@ -181,7 +181,7 @@
 			unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
 static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
 static void nand_sync (struct mtd_info *mtd);
-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,  struct nand_oobinfo *oobsel);
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,  struct nand_oobinfo *oobsel, int chipnr);
 
 static u_char nand_read_byte(struct mtd_info *mtd)
 {
@@ -379,7 +379,7 @@
 			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
 			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
 			/* One more address cycle for higher density devices */
-			if (mtd->size & 0x0c000000) 
+			if (this->chipsize & 0x0c000000) 
 				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
 		}
 		/* Latch in address */
@@ -463,7 +463,7 @@
 			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
 			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
 			/* One more address cycle for devices > 128MiB */
-			if (mtd->size > (128 << 20))
+			if (this->chipsize > (128 << 20))
 				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
 		}
 		/* Latch in address */
@@ -541,17 +541,7 @@
 			spin_unlock_bh (&this->chip_lock);
 		return;
 	}
-#if 0 /* This was broken. And of dubious utility */
-	if (this->state == FL_ERASING) {
-		if (new_state != FL_ERASING) {
-			this->state = new_state;
-			spin_unlock_bh (&this->chip_lock);
-			this->select_chip(mtd, 0);	/* select in any case */
-			this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-			return;
-		}
-	}
-#endif
+
 	set_current_state (TASK_UNINTERRUPTIBLE);
 	add_wait_queue (&this->wq, &wait);
 	spin_unlock_bh (&this->chip_lock);
@@ -608,7 +598,7 @@
  *	This function will always program a full page of data
  *	If you call it with a non page aligned buffer, you're lost :)
  */
-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,  struct nand_oobinfo *oobsel)
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,  struct nand_oobinfo *oobsel,  int chipnr)
 {
 	int 	i, status;
 	u_char	ecc_code[6], *oob_data;
@@ -759,7 +749,7 @@
 	 * This is not a problem for all chips, but I have found a bunch of them.
 	 */
 	this->select_chip(mtd, -1);
-	this->select_chip(mtd, 0);
+	this->select_chip(mtd, chipnr);
 #endif
 	return 0;
 }
@@ -779,7 +769,7 @@
 static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
 			  size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
 {
-	int j, col, page, end, ecc;
+	int j, col, page, end, ecc, chipnr;
 	int erase_state = 0;
 	int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
 	struct nand_chip *this = mtd->priv;
@@ -810,7 +800,8 @@
 	nand_get_chip (this, mtd ,FL_READING, &erase_state);
 
 	/* Select the NAND device */
-	this->select_chip(mtd, 0);
+	chipnr = (int)((unsigned long)from / this->chipsize);
+	this->select_chip(mtd, chipnr);
 
 	/* First we calculate the starting page */
 	page = from >> this->page_shift;
@@ -822,7 +813,7 @@
 	ecc = mtd->eccsize;
 
 	/* Send the read command */
-	this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+	this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page & this->pagemask);
 	
 	/* Loop until all data read */
 	while (read < len) {
@@ -941,11 +932,19 @@
 				udelay (this->chip_delay);
 			else
 				while (!this->dev_ready(mtd));	
+			
+			/* Check, if we cross a chip boundary */
+			if (!(page & this->pagemask)) {
+				chipnr++;
+				this->select_chip(mtd, -1);
+				this->select_chip(mtd, chipnr);
+			}
+				
 			/* Check, if the chip supports auto page increment 
 			 * or if we have hit a block boundary. 
 			*/ 
-			if (unlikely (!NAND_CANAUTOINCR(this) || !(page & blockcheck)))
-				this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+			if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+				this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page & this->pagemask);
 		}
 	}
 
@@ -972,7 +971,7 @@
  */
 static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
 {
-	int i, col, page;
+	int i, col, page, chipnr;
 	int erase_state = 0;
 	struct nand_chip *this = mtd->priv;
 	int	blockcheck = (mtd->erasesize >> this->page_shift) - 1;
@@ -981,7 +980,8 @@
 
 	/* Shift to get page */
 	page = ((int) from) >> this->page_shift;
-
+	chipnr = (int)((unsigned long)from / this->chipsize);
+	
 	/* Mask to get column */
 	col = from & (mtd->oobsize - 1);
 
@@ -999,10 +999,10 @@
 	nand_get_chip (this, mtd , FL_READING, &erase_state);
 
 	/* Select the NAND device */
-	this->select_chip(mtd, 0);
+	this->select_chip(mtd, chipnr);
 
 	/* Send the read command */
-	this->cmdfunc (mtd, NAND_CMD_READOOB, col, page);
+	this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
 	/* 
 	 * Read the data, if we read more than one page
 	 * oob data, let the device transfer the data !
@@ -1028,13 +1028,21 @@
 				udelay (this->chip_delay);
 			else
 				while (!this->dev_ready(mtd));	
+
+			/* Check, if we cross a chip boundary */
+			if (!(page & this->pagemask)) {
+				chipnr++;
+				this->select_chip(mtd, -1);
+				this->select_chip(mtd, chipnr);
+			}
+				
 			/* Check, if the chip supports auto page increment 
 			 * or if we have hit a block boundary. 
 			*/ 
-			if (unlikely (!NAND_CANAUTOINCR(this) || !(page & blockcheck))) {
+			if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
 				/* For subsequent page reads set offset to 0 */
 			        this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page);
-			} 		
+			} 
 		}
 	}
 	/* De-select the NAND device */
@@ -1066,7 +1074,7 @@
 static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
 			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
 {
-	int page, ret = 0, oob = 0, written = 0;
+	int page, ret = 0, oob = 0, written = 0, chipnr;
 	struct nand_chip *this = mtd->priv;
 
 	DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@@ -1089,12 +1097,13 @@
 
 	/* Shift to get page */
 	page = ((int) to) >> this->page_shift;
+	chipnr = (int)((unsigned long)to / this->chipsize);
 
 	/* Grab the lock and see if the device is available */
 	nand_get_chip (this, mtd, FL_WRITING, NULL);
 
 	/* Select the NAND device */
-	this->select_chip(mtd, 0);
+	this->select_chip(mtd, chipnr);
 
 	/* Check the WP bit */
 	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
@@ -1110,10 +1119,10 @@
 		this->data_poi = (u_char*) &buf[written];
 		/* We use the same function for write and writev */
 		if (eccbuf) {
-			ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel);
+			ret = nand_write_page (mtd, this, page & this->pagemask, &eccbuf[oob], oobsel, chipnr);
 			oob += mtd->oobsize;
 		} else 
-			ret = nand_write_page (mtd, this, page, NULL, oobsel);	
+			ret = nand_write_page (mtd, this, page & this->pagemask, NULL, oobsel, chipnr);	
 		
 		if (ret)
 			goto out;
@@ -1122,6 +1131,13 @@
 		written += cnt;
 		/* Increment page address */
 		page++;
+
+		/* Check, if we cross a chip boundary */
+		if (!(page & this->pagemask)) {
+			chipnr++;
+			this->select_chip(mtd, -1);
+			this->select_chip(mtd, chipnr);
+		}
 	}
 
 out:
@@ -1154,7 +1170,7 @@
  */
 static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
 {
-	int column, page, status, ret = 0;
+	int column, page, status, ret = 0, chipnr;
 	struct nand_chip *this = mtd->priv;
 #ifdef CONFIG_MTD_NAND_VERIFY_WRITE
 	int i;
@@ -1164,6 +1180,7 @@
 
 	/* Shift to get page */
 	page = ((int) to) >> this->page_shift;
+	chipnr = (int)((unsigned long)to / this->chipsize);
 
 	/* Mask to get column */
 	column = to & (mtd->oobsize - 1);
@@ -1181,7 +1198,7 @@
 	nand_get_chip (this, mtd, FL_WRITING, NULL);
 
 	/* Select the NAND device */
-	this->select_chip(mtd, 0);
+	this->select_chip(mtd, chipnr);
 
 	/* Reset the chip. Some chips (like the Toshiba TC5832DC found
 	   in one of my DiskOnChip 2000 test units) will clear the whole
@@ -1200,7 +1217,7 @@
 	
 	if (NAND_MUST_PAD(this)) {
 		/* Write out desired data */
-		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page);
+		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
 		/* prepad 0xff for partial programming */
 		this->write_buf(mtd, ffchars, column);
 		/* write data */
@@ -1209,7 +1226,7 @@
 		this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
 	} else {
 		/* Write out desired data */
-		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page);
+		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
 		/* write data */
 		this->write_buf(mtd, buf, len);
 	}
@@ -1229,7 +1246,7 @@
 
 #ifdef CONFIG_MTD_NAND_VERIFY_WRITE
 	/* Send command to read back the data */
-	this->cmdfunc (mtd, NAND_CMD_READOOB, column, page);
+	this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
 
 	/* Loop through and verify the data */
 	for (i = 0; i < len; i++) {
@@ -1267,7 +1284,7 @@
 static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, 
 		loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
 {
-	int i, page, len, total_len, ret = 0, written = 0;
+	int i, page, len, total_len, ret = 0, written = 0, chipnr;
 	struct nand_chip *this = mtd->priv;
 
 	/* Calculate total length of data */
@@ -1296,12 +1313,13 @@
 
 	/* Shift to get page */
 	page = ((int) to) >> this->page_shift;
+	chipnr = (int)((unsigned long)to / this->chipsize);
 
 	/* Grab the lock and see if the device is available */
 	nand_get_chip (this, mtd, FL_WRITING, NULL);
 
 	/* Select the NAND device */
-	this->select_chip(mtd, 0);
+	this->select_chip(mtd, chipnr);
 
 	/* Check the WP bit */
 	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
@@ -1350,7 +1368,7 @@
 		}
 		
 		/* We use the same function for write and writev !) */
-		ret = nand_write_page (mtd, this, page, NULL, oobsel);
+		ret = nand_write_page (mtd, this, page & this->pagemask, NULL, oobsel, chipnr);
 		if (ret)
 			goto out;
 
@@ -1359,6 +1377,13 @@
 
 		/* Increment page address */
 		page++;
+
+		/* Check, if we cross a chip boundary */
+		if (!(page & this->pagemask)) {
+			chipnr++;
+			this->select_chip(mtd, -1);
+			this->select_chip(mtd, chipnr);
+		}
 	}
 
 out:
@@ -1380,7 +1405,7 @@
  */
 static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
 {
-	int page, len, status, pages_per_block, ret;
+	int page, len, status, pages_per_block, ret, chipnr;
 	struct nand_chip *this = mtd->priv;
 	DECLARE_WAITQUEUE (wait, current);
 
@@ -1410,12 +1435,13 @@
 
 	/* 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, 0);
+	this->select_chip(mtd, chipnr);
 
 	/* Check the WP bit */
 	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
@@ -1459,6 +1485,12 @@
 			/* Increment page address and decrement length */
 			len -= mtd->erasesize;
 			page += pages_per_block;
+			/* Check, if we cross a chip boundary */
+			if (!(page & this->pagemask)) {
+				chipnr++;
+				this->select_chip(mtd, -1);
+				this->select_chip(mtd, chipnr);
+			}
 		}
 		/* Release the spin lock */
 		spin_unlock_bh (&this->chip_lock);
@@ -1468,7 +1500,7 @@
 		if (this->state == FL_ERASING || this->state == FL_READY) {
 			/* Select the NAND device again, if we were interrupted */
 			this->state = FL_ERASING;
-			this->select_chip(mtd, 0);
+			this->select_chip(mtd, chipnr);
 			continue;
 		} else {
 			set_current_state (TASK_UNINTERRUPTIBLE);
@@ -1605,7 +1637,7 @@
 			continue;
 			
 		mtd->name = nand_flash_ids[i].name;
-		mtd->size = nand_flash_ids[i].chipsize << 20;
+		this->chipsize = nand_flash_ids[i].chipsize << 20;
 		
 		/* New devices have all the information in additional id bytes */
 		if (!nand_flash_ids[i].pagesize) {
@@ -1669,8 +1701,6 @@
 		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;
 	}
 
@@ -1692,7 +1722,13 @@
 	}
 	if (i > 1)
 		printk(KERN_INFO "%d NAND chips detected\n", i);
-
+	
+	/* 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;
+	
 	/* 
 	 * check ECC mode, default to software
 	 * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize




More information about the linux-mtd-cvs mailing list