mtd/drivers/mtd/nand diskonchip.c,1.9,1.10

anoncvs anoncvs at infradead.org
Wed Jun 16 12:35:18 EDT 2004


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

Modified Files:
	diskonchip.c 
Log Message:
Fix pipeline problems. Add INFTL BBT support. Add ECC support. Add INFTL auto-partition support.


Index: diskonchip.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/nand/diskonchip.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- diskonchip.c	27 Mar 2004 19:55:53 -0000	1.9
+++ diskonchip.c	16 Jun 2004 16:35:16 -0000	1.10
@@ -20,6 +20,8 @@
 #include <linux/mtd/nand.h>
 #include <linux/mtd/doc2000.h>
 #include <linux/mtd/compatmac.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/inftl.h>
 
 struct doc_priv {
 	unsigned long virtadr;
@@ -31,6 +33,16 @@
 	int curchip;
 };
 
+static char inftl_bbt_pattern[] = "MSYS_BBT";
+
+static struct nand_bbt_descr inftl_bbt_descr = {
+        .options =NAND_BBT_LASTBLOCK | NAND_BBT_8BIT,
+        .offs =8,
+        .len = 8,
+        .maxblocks = 4,
+        .pattern = inftl_bbt_pattern
+};
+
 #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
 #define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
 
@@ -110,10 +122,12 @@
 	unsigned long docptr = doc->virtadr;
 
 	ReadDOC(docptr, CDSNSlowIO);
+	DoC_Delay(doc, 2);
 	u_char ret = ReadDOC(docptr, 2k_CDSN_IO);
 	if (debug) printk("read_byte returns %02x\n", ret);
 	return ret;
 }
+
 static void doc2000_writebuf(struct mtd_info *mtd, 
 			     const u_char *buf, int len)
 {
@@ -180,7 +194,7 @@
 	return 0;
 }
 
-static uint16_t doc200x_ident_chip(struct mtd_info *mtd, int nr)
+static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
 {
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = (void *)this->priv;
@@ -224,7 +238,7 @@
 	return ret;
 }
 
-static void doc2000_count_chips(struct mtd_info *mtd)
+static void __init doc2000_count_chips(struct mtd_info *mtd)
 {
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = (void *)this->priv;
@@ -276,11 +290,12 @@
 	struct doc_priv *doc = (void *)this->priv;
 	unsigned long docptr = doc->virtadr;
 
-	ReadDOC(docptr, CDSNSlowIO);
+	//ReadDOC(docptr, CDSNSlowIO);
 	/* 11.4.5 -- delay twice to allow extended length cycle */
 	DoC_Delay(doc, 2);
 	ReadDOC(docptr, ReadPipeInit);
-	return ReadDOC(docptr, Mil_CDSN_IO);
+	//return ReadDOC(docptr, Mil_CDSN_IO);
+	return ReadDOC(docptr, LastDataRead);
 }
 
 static void doc2001_writebuf(struct mtd_info *mtd, 
@@ -314,6 +329,7 @@
 	/* Terminate read pipeline */
 	buf[i] = ReadDOC(docptr, LastDataRead);
 }
+
 static int doc2001_verifybuf(struct mtd_info *mtd, 
 			     const u_char *buf, int len)
 {
@@ -425,30 +441,250 @@
 	return 0;
 }
 
-struct doc_priv mydoc = {
-	.physadr = 0xd4000,
+static int doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+
+	/* Prime the ECC engine */
+	WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+	if (mode == NAND_ECC_READ)
+		WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+	else
+		WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+	return 0;
+}
+
+static int doc2000_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+				 unsigned char *ecc_code)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+	volatile char dummy;
+	int stat;
+	int i;
+	// DBB : check for NULL ecc_code here?
+
+	// DBB note: I'm using the presence of dat as a read/write indicator.  FIX ME!
+	if (dat) {
+		// DBB: this is assumed to be a read
+		// flush the pipeline:
+		dummy = ReadDOC(docptr, 2k_ECCStatus);
+		dummy = ReadDOC(docptr, 2k_ECCStatus);
+		dummy = ReadDOC(docptr, 2k_ECCStatus);
+		stat = dummy & 0x80;
+	} else {
+		// DBB: this is assumed to be a write
+		WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl);
+		WriteDOC(0, docptr, 2k_CDSN_IO);
+		WriteDOC(0, docptr, 2k_CDSN_IO);
+		WriteDOC(0, docptr, 2k_CDSN_IO);
+		WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+		stat = 1;
+	}
+	if (stat) {
+		for (i = 0; i < 6; i++)
+			ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+	} else {
+		/* On read, if there were no ECC errors, the syndrome is by
+		   definition zero.  We can skip actually reading it. */
+		memset(ecc_code, 0, 6);
+	}
+	WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+	return stat;
+}
+
+static int doc2001_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+				 unsigned char *ecc_code)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+	volatile char dummy;
+	int stat;
+	int i;
+	// DBB : check for NULL ecc_code here?
+
+	// DBB note: I'm using the presence of dat as a read/write indicator.  FIX ME!
+	if (dat) {
+		// DBB: this is assumed to be a read
+		// flush the pipeline:
+		dummy = ReadDOC(docptr, ECCConf);
+		dummy = ReadDOC(docptr, ECCConf);
+		dummy = ReadDOC(docptr, ECCConf);
+		stat = dummy & 0x80;
+	} else {
+		// DBB: this is assumed to be a write
+		WriteDOC(0, docptr, NOP);
+		WriteDOC(0, docptr, NOP);
+		WriteDOC(0, docptr, NOP);
+		stat = 1;
+	}
+	if (stat) {
+		for (i = 0; i < 6; i++)
+			ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+	} else {
+		/* On read, if there were no ECC errors, the syndrome is by
+		   definition zero.  We can skip actually reading it. */
+		memset(ecc_code, 0, 6);
+	}
+	WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+	return stat;
+}
+
+static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+{
+	int ret = doc_decode_ecc(dat, calc_ecc);
+	printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
+	return ret;
+}
+		
+static struct doc_priv mydoc = {
+	.physadr = 0xd0000,
 	.curfloor = -1,
 	.curchip = -1,
 };
 
-u_char mydatabuf[528];
+//u_char mydatabuf[528];
 
-struct nand_chip mynand = {
+static struct nand_oobinfo doc200x_oobinfo = {
+        .useecc = MTD_NANDECC_AUTOPLACE,
+        .eccbytes = 6,
+        .eccpos = {0, 1, 2, 3, 4, 5},
+        .oobfree = { {8, 8} }
+};
+ 
+static struct nand_chip mynand = {
 	.priv = (void *)&mydoc,
 	.select_chip = doc200x_select_chip,
 	.hwcontrol = doc200x_hwcontrol,
 	.dev_ready = doc200x_dev_ready,
 	.waitfunc = doc200x_wait,
 	.block_bad = doc200x_block_bad,
-	.eccmode = NAND_ECC_SOFT,
-	.data_buf = mydatabuf,
+	.eccmode = NAND_ECC_DISKONCHIP,
+	//.data_buf = mydatabuf,
+	.options = NAND_USE_FLASH_BBT,
+	.autooob = &doc200x_oobinfo,
+	.correct_data = doc200x_correct_data,
+	.enable_hwecc = doc200x_enable_hwecc
 };
 
-struct mtd_info mymtd = {
+static struct mtd_info mymtd = {
 	.priv = (void *)&mynand,
 	.owner = THIS_MODULE,
 };
 
+/* This is a stripped-down copy of the code in inftlmount.c */
+static int __init inftl_partscan(struct mtd_info *mtd)
+{
+	u_char buf[SECTORSIZE];
+	struct INFTLMediaHeader *mh = (struct INFTLMediaHeader *) &buf;
+	int offs;
+	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;
+	}
+
+	mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
+	mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
+	mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
+	mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
+	mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
+	mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
+ 
+//#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,
+		mh->bootRecordID, mh->NoOfBootImageBlocks,
+		mh->NoOfBinaryPartitions,
+		mh->NoOfBDTLPartitions,
+		mh->BlockMultiplierBits, mh->FormatFlags,
+		mh->OsakVersion, mh->PercentUsed);
+//#endif
+
+	if (mh->BlockMultiplierBits != 0) {
+		printk(KERN_ERR "Currently only BlockMultiplierBits=0 is supported.\n");
+		return 0;
+	}
+
+	struct mtd_partition parts[6];
+	memset((char *) parts, 0, sizeof(parts));
+	int numparts = 0;
+	int lastblock = 0;
+
+	/* Scan the partitions */
+	int i;
+	struct INFTLPartition *ip;
+	for (i = 0; (i < 4); i++) {
+		ip = &(mh->Partitions[i]);
+		ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
+		ip->firstUnit = le32_to_cpu(ip->firstUnit);
+		ip->lastUnit = le32_to_cpu(ip->lastUnit);
+		ip->flags = le32_to_cpu(ip->flags);
+		ip->spareUnits = le32_to_cpu(ip->spareUnits);
+		ip->Reserved0 = le32_to_cpu(ip->Reserved0);
+
+//#ifdef CONFIG_MTD_DEBUG_VERBOSE
+//		if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+		printk(KERN_INFO	"    PARTITION[%d] ->\n"
+			"        virtualUnits    = %d\n"
+			"        firstUnit       = %d\n"
+			"        lastUnit        = %d\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);
+		numparts++;
+		if (ip->lastUnit > lastblock) lastblock = ip->lastUnit;
+		if (ip->flags & INFTL_LAST) break;
+	}
+	lastblock++;
+	if ((mtd->erasesize * lastblock) < mtd->size) {
+		parts[numparts].name = "DiskOnChip Remainder partition";
+		parts[numparts].offset = mtd->erasesize * lastblock;
+		parts[numparts].size = mtd->size - parts[numparts].offset;
+		numparts++;
+	}
+	add_mtd_partitions(mtd, parts, numparts);
+	return 1;
+}
+
+
 int __init init_nanddoc(void)
 {
 	mydoc.virtadr = (unsigned long)ioremap(mydoc.physadr, DOC_IOREMAP_LEN);
@@ -474,6 +710,9 @@
 		mynand.write_buf = doc2001_writebuf;
 		mynand.read_buf = doc2001_readbuf;
 		mynand.verify_buf = doc2001_verifybuf;
+                mynand.bbt_td = &inftl_bbt_descr;
+		mynand.calculate_ecc = doc2001_calculate_ecc;
+		//mynand.scan_bbt = nftl_scan_bbt;
 
 		ReadDOC(mydoc.virtadr, ChipID);
 		ReadDOC(mydoc.virtadr, ChipID);
@@ -499,11 +738,12 @@
 		mynand.write_buf = doc2000_writebuf;
 		mynand.read_buf = doc2000_readbuf;
 		mynand.verify_buf = doc2000_verifybuf;
+		mynand.calculate_ecc = doc2000_calculate_ecc;
 
 		doc2000_count_chips(&mymtd);
 		nrchips = 4 * mydoc.chips_per_floor;
 		name = "DiskOnChip 2000 (NFTL Model)";
-		mydoc.CDSNControl |= CDSN_CTRL_FLASH_IO;
+		mydoc.CDSNControl |= CDSN_CTRL_FLASH_IO|CDSN_CTRL_ECC_IO;
 
 		break;
 
@@ -516,12 +756,14 @@
 	}
 	mymtd.name = name;
 	add_mtd_device(&mymtd);
+	if (DoC_is_Millennium(&mydoc)) inftl_partscan(&mymtd);
 
 	return 0;
 }
 
 void __exit cleanup_nanddoc(void)
 {
+	del_mtd_partitions(&mymtd);
 	del_mtd_device(&mymtd);
 	iounmap((void *)mydoc.virtadr);
 }





More information about the linux-mtd-cvs mailing list