mtd/drivers/mtd/nand diskonchip.c,1.14,1.15

dbrown at infradead.org dbrown at infradead.org
Tue Jun 22 10:08:16 EDT 2004


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

Modified Files:
	diskonchip.c 
Log Message:
Add BBT and autopartition support for NFTL devices.


Index: diskonchip.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/nand/diskonchip.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -r1.14 -r1.15
--- diskonchip.c	18 Jun 2004 22:56:48 -0000	1.14
+++ diskonchip.c	22 Jun 2004 14:08:13 -0000	1.15
@@ -31,8 +31,15 @@
 	int chips_per_floor; /* The number of chips detected on each floor */
 	int curfloor;
 	int curchip;
+	int mh0_page;
+	int mh1_page;
 };
 
+/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL
+   MediaHeader.  The spec says to just keep going, I think, but that's just
+   silly. */
+#define MAX_MEDIAHEADER_SCAN 8
+
 /* This is the syndrome computed by the HW ecc generator upon reading an empty
    page, one with all 0xff for data and stored ecc code. */
 static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
@@ -40,14 +47,30 @@
    page, one with all 0xff for data. */
 static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
 
-static char inftl_bbt_pattern[] = "MSYS_BBT";
+#define INFTL_BBT_RESERVED_BLOCKS 4
+
+static struct nand_bbt_descr inftl_bbt_descr0 = {
+        .options =NAND_BBT_LASTBLOCK | NAND_BBT_8BIT,
+        .offs =8,
+        .len = 8,
+        .maxblocks = INFTL_BBT_RESERVED_BLOCKS,
+        .pattern = "MSYS_BBT"
+};
 
-static struct nand_bbt_descr inftl_bbt_descr = {
+static struct nand_bbt_descr inftl_bbt_descr1 = {
         .options =NAND_BBT_LASTBLOCK | NAND_BBT_8BIT,
         .offs =8,
         .len = 8,
-        .maxblocks = 4,
-        .pattern = inftl_bbt_pattern
+        .maxblocks = INFTL_BBT_RESERVED_BLOCKS,
+        .pattern = "TBB_SYSM"
+};
+
+static struct nand_bbt_descr nftl_bbt_descr0 = {
+	.options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | NAND_BBT_SAVECONTENT | NAND_BBT_WRITE
+};
+
+static struct nand_bbt_descr nftl_bbt_descr1 = {
+	.options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | NAND_BBT_SAVECONTENT | NAND_BBT_WRITE
 };
 
 #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
@@ -65,6 +88,12 @@
 static int no_ecc_failures=0;
 MODULE_PARM(no_ecc_failures, "i");
 
+static int no_autopart=0;
+MODULE_PARM(no_autopart, "i");
+
+static int inftl_bbt_write=0;
+MODULE_PARM(inftl_bbt_write, "i");
+
 static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
 {
 	volatile char dummy;
@@ -503,16 +532,19 @@
 	WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
 	/* If emptymatch=1, we might have an all-0xff data buffer.  Check. */
 	if (emptymatch) {
+		/* Note: this somewhat expensive test should not be triggered
+		   often.  It could be optimized away by examining the data in
+		   the readbuf routine, and remembering the result. */
 		for (i = 0; i < 512; i++) {
 			if (dat[i] == 0xff) continue;
 			emptymatch = 0;
 			break;
 		}
-		/* If emptymatch=1 still, we do have an all-0xff data buffer.
-		   Return all-0xff ecc value instead of the computed one, so
-		   it'll look just like a freshly-erased page. */
-		if (emptymatch) memset(ecc_code, 0xff, 6);
 	}
+	/* If emptymatch still =1, we do have an all-0xff data buffer.
+	   Return all-0xff ecc value instead of the computed one, so
+	   it'll look just like a freshly-erased page. */
+	if (emptymatch) memset(ecc_code, 0xff, 6);
 	return 0;
 }
 
@@ -543,6 +575,8 @@
 			if (calc_ecc[i] != empty_read_syndrome[i])
 				emptymatch = 0;
 		}
+		/* If emptymatch=1, the read syndrome is consistent with an
+		   all-0xff data and stored ecc block.  Check the stored ecc. */
 		if (emptymatch) {
 			for (i = 0; i < 6; i++) {
 				if (read_ecc[i] == 0xff) continue;
@@ -550,7 +584,18 @@
 				break;
 			}
 		}
-		/* If emptymatch == 1, this is almost certainly a freshly-
+		/* If emptymatch still =1, check the data block. */
+		if (emptymatch) {
+		/* Note: this somewhat expensive test should not be triggered
+		   often.  It could be optimized away by examining the data in
+		   the writebuf routine, and remembering the result. */
+			for (i = 0; i < 512; i++) {
+				if (dat[i] == 0xff) continue;
+				emptymatch = 0;
+				break;
+			}
+		}
+		/* If emptymatch still =1, this is almost certainly a freshly-
 		   erased block, in which case the ECC will not come out right.
 		   We'll suppress the error and tell the caller everything's
 		   OK.  Because it is. */
@@ -602,30 +647,122 @@
 	.owner = THIS_MODULE,
 };
 
+/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
+   On sucessful return, buf will contain a copy of the media header for
+   further processing.  id is the string to scan for, and will presumably be
+   either "ANAND" or "BNAND".  If findmirror=1, also look for the mirror media
+   header.  The page #s of the found media headers are placed in mh0_page and
+   mh1_page in the DOC private structure. */
+static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
+				     const char *id, int findmirror)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	int offs, end = (MAX_MEDIAHEADER_SCAN << this->erase_shift);
+	int ret, retlen;
+
+	end = min(end, mtd->size); // paranoia
+	for (offs = 0; offs < end; offs += mtd->erasesize) {
+/* DBB note: This read will use ECC.  Is this OK?  Check with David. */
+		if ((ret = mtd->read(mtd, offs, SECTORSIZE, &retlen, buf)))
+			continue;
+		if (retlen != SECTORSIZE) continue;
+		if (memcmp(buf, id, 6)) continue;
+		printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
+		if (doc->mh0_page == -1) {
+			doc->mh0_page = offs >> this->page_shift;
+			if (!findmirror) return 1;
+			continue;
+		}
+		doc->mh1_page = offs >> this->page_shift;
+		return 2;
+	}
+	if (doc->mh0_page == -1) {
+		printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id);
+		return 0;
+	}
+	/* Only one mediaheader was found.  We want buf to contain a
+	   mediaheader on return, so we'll have to re-read the one we found. */
+	offs = doc->mh0_page << this->page_shift;
+	ret = mtd->read(mtd, offs, SECTORSIZE, &retlen, buf);
+	if (ret || (retlen != SECTORSIZE)) {
+		/* Insanity.  Give up. */
+		printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
+		return 0;
+	}
+	return 1;
+}
+
+static int __init nftl_partscan(struct mtd_info *mtd,
+				struct mtd_partition *parts)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	u_char *buf = this->data_buf;
+	struct NFTLMediaHeader *mh = (struct NFTLMediaHeader *) buf;
+	int offs;
+
+	if (!find_media_headers(mtd, buf, "ANAND", 1)) return 0;
+
+//#ifdef CONFIG_MTD_DEBUG_VERBOSE
+//	if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+	printk(KERN_INFO "    DataOrgID        = %s\n"
+			 "    NumEraseUnits    = %d\n"
+			 "    FirstPhysicalEUN = %d\n"
+			 "    FormattedSize    = %d\n"
+			 "    UnitSizeFactor   = %d\n",
+		mh->DataOrgID, mh->NumEraseUnits,
+		mh->FirstPhysicalEUN, mh->FormattedSize,
+		mh->UnitSizeFactor);
+//#endif
+
+	if (mh->UnitSizeFactor != 0xff) {
+		printk(KERN_ERR "Currently only UnitSizeFactor=0xff is supported.\n");
+		return 0;
+	}
+
+	/* Skip past the media headers. */
+	offs = max(doc->mh0_page, doc->mh1_page);
+	offs <<= this->page_shift;
+	offs += mtd->erasesize;
+
+	//parts[0].name = "DiskOnChip Boot / Media Header partition";
+	//parts[0].offset = 0;
+	//parts[0].size = offs;
+
+	parts[0].name = "DiskOnChip BDTL partition";
+	parts[0].offset = offs;
+	parts[0].size = mh->NumEraseUnits << this->erase_shift;
+
+	offs += parts[0].size;
+	if (offs < mtd->size) {
+		parts[1].name = "DiskOnChip Remainder partition";
+		parts[1].offset = offs;
+		parts[1].size = mtd->size - offs;
+		return 2;
+	}
+	return 1;
+}
+
 /* This is a stripped-down copy of the code in inftlmount.c */
-static int __init inftl_partscan(struct mtd_info *mtd)
+static int __init inftl_partscan(struct mtd_info *mtd,
+				 struct mtd_partition *parts)
 {
-	u_char buf[SECTORSIZE];
-	struct INFTLMediaHeader *mh = (struct INFTLMediaHeader *) &buf;
-	struct mtd_partition parts[6];
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	u_char *buf = this->data_buf;
+	struct INFTLMediaHeader *mh = (struct INFTLMediaHeader *) buf;
 	struct INFTLPartition *ip;
 	int numparts = 0;
 	int lastblock = 0;
 	int i;
-	int offs;
+	int end = mtd->size;
 
-	for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
-		int ret, retlen;
-                if ((ret = MTD_READ(mtd, offs, SECTORSIZE, &retlen, buf)))
-			continue;
-		if (retlen < sizeof(struct INFTLMediaHeader)) continue;
-//printk(KERN_ERR "Read %d bytes at %d, string is %s\n", retlen, offs, buf);
-		if (!memcmp(mh->bootRecordID, "BNAND", 6)) break;
-	}
-	if (offs >= mtd->size) {
-		printk(KERN_WARNING "INFTL Media Header not found.\n");
-		return 0;
-	}
+	if (inftl_bbt_write)
+		end -= (INFTL_BBT_RESERVED_BLOCKS << this->erase_shift);
+
+	if (!find_media_headers(mtd, buf, "BNAND", 0)) return 0;
+	doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
 
 	mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
 	mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
@@ -636,16 +773,14 @@
  
 //#ifdef CONFIG_MTD_DEBUG_VERBOSE
 //	if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
-	printk(KERN_INFO "Found INFTL Media Header at 0x%x:\n"
-			"    bootRecordID          = %s\n"
-			"    NoOfBootImageBlocks   = %d\n"
-			"    NoOfBinaryPartitions  = %d\n"
-			"    NoOfBDTLPartitions    = %d\n"
-			"    BlockMultiplerBits    = %d\n"
-			"    FormatFlgs            = %d\n"
-			"    OsakVersion           = 0x%x\n"
-			"    PercentUsed           = %d\n",
-		offs,
+	printk(KERN_INFO "    bootRecordID          = %s\n"
+			 "    NoOfBootImageBlocks   = %d\n"
+			 "    NoOfBinaryPartitions  = %d\n"
+			 "    NoOfBDTLPartitions    = %d\n"
+			 "    BlockMultiplerBits    = %d\n"
+			 "    FormatFlgs            = %d\n"
+			 "    OsakVersion           = 0x%x\n"
+			 "    PercentUsed           = %d\n",
 		mh->bootRecordID, mh->NoOfBootImageBlocks,
 		mh->NoOfBinaryPartitions,
 		mh->NoOfBDTLPartitions,
@@ -658,8 +793,6 @@
 		return 0;
 	}
 
-	memset((char *) parts, 0, sizeof(parts));
-
 	/* Scan the partitions */
 	for (i = 0; (i < 4); i++) {
 		ip = &(mh->Partitions[i]);
@@ -676,47 +809,104 @@
 			"        virtualUnits    = %d\n"
 			"        firstUnit       = %d\n"
 			"        lastUnit        = %d\n"
-			"        flags	         = 0x%x\n"
+			"        flags           = 0x%x\n"
 			"        spareUnits      = %d\n",
 			i, ip->virtualUnits, ip->firstUnit,
 			ip->lastUnit, ip->flags,
 			ip->spareUnits);
 //#endif
 
+/*
 		if ((i == 0) && (ip->firstUnit > 0)) {
 			parts[0].name = "DiskOnChip IPL / Media Header partition";
 			parts[0].offset = 0;
 			parts[0].size = mtd->erasesize * ip->firstUnit;
 			numparts = 1;
 		}
+*/
 
 		if (ip->flags & INFTL_BINARY)
 			parts[numparts].name = "DiskOnChip BDK partition";
 		else
 			parts[numparts].name = "DiskOnChip BDTL partition";
-		parts[numparts].offset = mtd->erasesize * ip->firstUnit;
-		parts[numparts].size = mtd->erasesize * (1 + ip->lastUnit - ip->firstUnit);
+		parts[numparts].offset = ip->firstUnit << this->erase_shift;
+		parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << this->erase_shift;
 		numparts++;
 		if (ip->lastUnit > lastblock) lastblock = ip->lastUnit;
 		if (ip->flags & INFTL_LAST) break;
 	}
 	lastblock++;
-	if ((mtd->erasesize * lastblock) < mtd->size) {
+	if ((lastblock << this->erase_shift) < end) {
 		parts[numparts].name = "DiskOnChip Remainder partition";
-		parts[numparts].offset = mtd->erasesize * lastblock;
-		parts[numparts].size = mtd->size - parts[numparts].offset;
+		parts[numparts].offset = lastblock << this->erase_shift;
+		parts[numparts].size = end - parts[numparts].offset;
 		numparts++;
 	}
-	add_mtd_partitions(mtd, parts, numparts);
-	return 1;
+	return numparts;
+}
+
+int __init nftl_scan_bbt(struct mtd_info *mtd)
+{
+	int ret, numparts;
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	struct mtd_partition parts[2];
+
+	if (this->numchips > doc->chips_per_floor) {
+		printk(KERN_ERR "Multi-floor devices not yet supported.\n");
+		return -EIO;
+	}
+	memset((char *) parts, 0, sizeof(parts));
+	/* On NFTL, we have to find the media headers before we can read the
+	   BBTs, since they're stored in the media header eraseblocks. */
+	numparts = nftl_partscan(mtd, parts);
+	if (!numparts) return -EIO;
+	this->bbt_td->pages[0] = doc->mh0_page + 1;
+	if (doc->mh1_page == -1)
+		this->bbt_md = NULL;
+	else
+		this->bbt_md->pages[0] = doc->mh1_page + 1;
+	/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
+	   At least as nand_bbt.c is currently written. */
+	if (ret = nand_scan_bbt(mtd, NULL)) return ret;
+	add_mtd_device(mtd);
+	if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);
+	return 0;
 }
 
+int __init inftl_scan_bbt(struct mtd_info *mtd)
+{
+	int ret, numparts;
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	struct mtd_partition parts[5];
+
+	if (this->numchips > doc->chips_per_floor) {
+		printk(KERN_ERR "Multi-floor devices not yet supported.\n");
+		return -EIO;
+	}
+	if (inftl_bbt_write) {
+		this->bbt_td->options |= NAND_BBT_WRITE;
+		this->bbt_md->options |= NAND_BBT_WRITE;
+	}
+	/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
+	   At least as nand_bbt.c is currently written. */
+	if (ret = nand_scan_bbt(mtd, NULL)) return ret;
+	memset((char *) parts, 0, sizeof(parts));
+	numparts = inftl_partscan(mtd, parts);
+	/* At least for now, require the INFTL Media Header. */
+	if (!numparts) return -EIO;
+	add_mtd_device(mtd);
+	if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);
+	return 0;
+}
 
 int __init init_nanddoc(void)
 {
+	int ret;
 	int nrchips = 1;
-	char *name;
 	mydoc.virtadr = (unsigned long)ioremap(mydoc.physadr, DOC_IOREMAP_LEN);
+	mydoc.mh0_page = mydoc.mh1_page = -1;
 
 	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, 
 		 mydoc.virtadr, DOCControl);
@@ -737,8 +927,9 @@
 		mynand.write_buf = doc2001_writebuf;
 		mynand.read_buf = doc2001_readbuf;
 		mynand.verify_buf = doc2001_verifybuf;
-                mynand.bbt_td = &inftl_bbt_descr;
-		//mynand.scan_bbt = nftl_scan_bbt;
+                mynand.bbt_td = &inftl_bbt_descr0;
+                mynand.bbt_md = &inftl_bbt_descr1;
+		mynand.scan_bbt = inftl_scan_bbt;
 
 		ReadDOC(mydoc.virtadr, ChipID);
 		ReadDOC(mydoc.virtadr, ChipID);
@@ -749,12 +940,12 @@
 			   can have multiple chips. */
 			doc2000_count_chips(&mymtd);
 			nrchips = 4 * mydoc.chips_per_floor;
-			name = "DiskOnChip 2000 (INFTL Model)";
+			mymtd.name = "DiskOnChip 2000 (INFTL Model)";
 		} else {
 			/* Bog-standard Millennium */
 			mydoc.chips_per_floor = 1;
 			nrchips = 1;
-			name = "DiskOnChip Millennium";
+			mymtd.name = "DiskOnChip Millennium";
 		}
 		break;
 
@@ -764,10 +955,13 @@
 		mynand.write_buf = doc2000_writebuf;
 		mynand.read_buf = doc2000_readbuf;
 		mynand.verify_buf = doc2000_verifybuf;
+                mynand.bbt_td = &nftl_bbt_descr0;
+                mynand.bbt_md = &nftl_bbt_descr1;
+		mynand.scan_bbt = nftl_scan_bbt;
 
 		doc2000_count_chips(&mymtd);
 		nrchips = 4 * mydoc.chips_per_floor;
-		name = "DiskOnChip 2000 (NFTL Model)";
+		mymtd.name = "DiskOnChip 2000 (NFTL Model)";
 		mydoc.CDSNControl |= CDSN_CTRL_FLASH_IO|CDSN_CTRL_ECC_IO;
 
 		break;
@@ -775,21 +969,23 @@
 	default:
 		return -EIO;
 	}
-	if (nand_scan(&mymtd, nrchips)) {
+	if (ret = nand_scan(&mymtd, nrchips)) {
+		/* DBB note: i believe nand_release is necessary here, as
+		   buffers may have been allocated in nand_base.  Check with
+		   Thomas. FIX ME! */
+		/* nand_release will call del_mtd_device, but we haven't yet
+		   added it.  This is handled without incident by
+		   del_mtd_device, as far as I can tell. */
+		nand_release(&mymtd);
 		iounmap((void *)mydoc.virtadr);
-		return -EIO;
 	}
-	mymtd.name = name;
-	add_mtd_device(&mymtd);
-	if (DoC_is_Millennium(&mydoc)) inftl_partscan(&mymtd);
 
-	return 0;
+	return ret;
 }
 
 void __exit cleanup_nanddoc(void)
 {
-	del_mtd_partitions(&mymtd);
-	del_mtd_device(&mymtd);
+	nand_release(&mymtd);
 	iounmap((void *)mydoc.virtadr);
 }
 	





More information about the linux-mtd-cvs mailing list