public inbox for linux-mtd@lists.infradead.org
 help / color / mirror / Atom feed
* [PATCH] Towards DiskOnChip support as a NAND driver
@ 2004-06-16 14:19 Dan Brown
  2004-06-16 22:32 ` Thomas Gleixner
  0 siblings, 1 reply; 7+ messages in thread
From: Dan Brown @ 2004-06-16 14:19 UTC (permalink / raw)
  To: Thomas Gleixner; +Cc: linux-mtd

[-- Attachment #1: Type: text/plain, Size: 2099 bytes --]

For review and testing, here is a patch which brings us a step closer to
proper support for DiskOnChip devices.  This is against the 20040615
snapshot.

This patch includes the following:

drivers/mtd/nand/diskonchip.c:
    - Fix pipeline problems for DOC2000 and Millenium/2000TSOP (only tested
on 2000TSOP)
    - Add bad block table (read-only) support for INFTL-based devices (only
2000TSOP for the moment)
    - Add hardware ECC support for DOC2000 and Millenium/2000TSOP (only
tested on 2000TSOP and needs more testing!)
    - Add auto-partitioning for INFTL-based devices (should be made
optional?)

drivers/mtd/nand/nand_base.c:
    - Add support for DiskOnChip hardware ECC
    - Fix a null-pointer ref that occurs if a NAND driver supplies a primary
but not a mirror BBT descriptor

include/linux/mtd/nand.h:
    - Add a return value to calculate_ecc.  This is used by nand_base to
skip the call to correct_data if we know there are no errors.  (This saves a
6-byte comparison-against-zero when using DOC.  Is it worth changing the API
for it?  You tell me.)

-----------------------------------------------------------

Please realize that this needs a lot more testing.  I still haven't gotten
JFFS2 to run properly with this (though I suspect that may reflect my
inexperience with JFFS2).  Also, the change to nand.h may break other nand
drivers (though the fix is trivial).

Things that aren't done yet:
    - BBT support for NFTL-based devices (coming soon!)
    - BBT updating (not important when using (I)NFTL filesystem, but matters
for JFFS2)
    - Auto-partitioning for NFTL-based devices?
    - DOC Millenium Plus support
    - Proper BBT handling for multi-floor INFTL devices (don't try it with
the current code!)
    - Modifying (I)NFTL to use the nand-based DiskOnChip driver
    - Modifying JFFS2 to use autooob layout?  (Not sure about this)
    - Modifying flash_eraseall to use autooob layout?  (Should work OK on
DOC by coincidence)
    - Adding the nand/diskonchip driver into the kernel build system instead
of the old one

    - More testing.

Enjoy!

    -Dan Brown

[-- Attachment #2: docnand.diff --]
[-- Type: application/octet-stream, Size: 16074 bytes --]

diff -u --recursive mtd-20040615/drivers/mtd/nand/diskonchip.c mtd/drivers/mtd/nand/diskonchip.c
--- mtd-20040615/drivers/mtd/nand/diskonchip.c	2004-03-27 18:00:08.000000000 -0500
+++ mtd/drivers/mtd/nand/diskonchip.c	2004-06-16 07:09:34.000000000 -0400
@@ -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_ERR "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(	"    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);
 }
diff -u --recursive mtd-20040615/drivers/mtd/nand/nand_base.c mtd/drivers/mtd/nand/nand_base.c
--- mtd-20040615/drivers/mtd/nand/nand_base.c	2004-06-07 18:00:15.000000000 -0400
+++ mtd/drivers/mtd/nand/nand_base.c	2004-06-16 07:37:48.000000000 -0400
@@ -830,6 +830,7 @@
 	case NAND_ECC_HW8_512:	
 		eccbytes += 2;
 	/* Hardware ecc 6 byte / 512 byte data */	
+	case NAND_ECC_DISKONCHIP:
 	case NAND_ECC_HW6_512:	
 		eccbytes += 3;
 	/* Hardware ecc 3 byte / 256 data */	
@@ -1013,6 +1014,7 @@
 	int	*oob_config, datidx;
 	int	blockcheck = (mtd->erasesize >> this->page_shift) - 1;
 	int	eccbytes = 3;
+	int	do_correct_data;
 
 
 	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
@@ -1053,6 +1055,7 @@
 	ecc = this->eccsize;
 	switch (eccmode) {
 	case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data  */
+	case NAND_ECC_DISKONCHIP:
 		eccbytes = 6;
 		break;						
 	case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data  */
@@ -1096,6 +1099,8 @@
 			oob_data = &this->data_buf[end];
 
 		eccsteps = this->eccsteps;
+		do_correct_data = 1;
+		ecc_status = 0;
 		
 		switch (eccmode) {
 		case NAND_ECC_NONE: {	/* No ECC, Read in a page */
@@ -1125,13 +1130,29 @@
 			}
 			break;						
 
+		case NAND_ECC_DISKONCHIP:
+			/* On DOC, we have to read data + OOB-stored syndrome,
+			   then read the hw-computed syndrome, then the rest
+			   of the OOB data. */
+			this->enable_hwecc(mtd, NAND_ECC_READ);	
+			this->read_buf(mtd, data_poi, ecc);
+			this->read_buf(mtd, oob_data, eccbytes);
+			/* On DOC (at least), we have a ECC error register bit
+			   that allows us to skip the tedious process of reading
+			   the hw-computed syndrome, checking for and correcting
+			   errors, etc. */
+			do_correct_data = this->calculate_ecc(mtd, data_poi, ecc_calc);
+			break;
 		default:
 			printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
 			BUG();	
 		}
 
 		/* read oobdata */
-		this->read_buf(mtd, oob_data, mtd->oobsize);
+		if (eccmode == NAND_ECC_DISKONCHIP)
+			this->read_buf(mtd, oob_data + eccbytes, mtd->oobsize - eccbytes);
+		else
+			this->read_buf(mtd, oob_data, mtd->oobsize);
 		
 		/* Skip ECC, if not active */
 		if (eccmode == NAND_ECC_NONE)
@@ -1143,7 +1164,8 @@
 
 		/* correct data, if neccecary */
 		for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
-			ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
+			if (do_correct_data)
+				ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
 			
 			/* Get next chunk of ecc bytes */
 			j += eccbytes;
@@ -2384,6 +2406,7 @@
 	case NAND_ECC_HW3_512: 
 	case NAND_ECC_HW6_512: 
 	case NAND_ECC_HW8_512: 
+	case NAND_ECC_DISKONCHIP:
 		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;
diff -u --recursive mtd-20040615/drivers/mtd/nand/nand_bbt.c mtd/drivers/mtd/nand/nand_bbt.c
--- mtd-20040615/drivers/mtd/nand/nand_bbt.c	2004-06-04 18:00:12.000000000 -0400
+++ mtd/drivers/mtd/nand/nand_bbt.c	2004-06-16 07:40:34.000000000 -0400
@@ -794,7 +794,7 @@
 	
 	/* Prevent the bbt regions from erasing / writing */
 	mark_bbt_region (mtd, td);
-	mark_bbt_region (mtd, md);
+	if (md) mark_bbt_region (mtd, md);
 	
 	kfree (buf);
 	return res;
diff -u --recursive mtd-20040615/include/linux/mtd/nand.h mtd/include/linux/mtd/nand.h
--- mtd-20040615/include/linux/mtd/nand.h	2004-06-04 18:00:13.000000000 -0400
+++ mtd/include/linux/mtd/nand.h	2004-06-16 07:49:07.000000000 -0400
@@ -281,7 +281,7 @@
 	int  		(*dev_ready)(struct mtd_info *mtd);
 	void 		(*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
 	int 		(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
-	void		(*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+	int 		(*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
 	int 		(*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
 	void		(*enable_hwecc)(struct mtd_info *mtd, int mode);
 	void		(*erase_cmd)(struct mtd_info *mtd, int page);

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2004-06-16 22:56 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <E1BacpZ-0008CJ-KT@canuck.infradead.org>
2004-06-16 16:13 ` [PATCH] Towards DiskOnChip support as a NAND driver Slim
2004-06-16 20:23   ` Dan Brown
2004-06-16 21:47     ` Thomas Gleixner
2004-06-16 14:19 Dan Brown
2004-06-16 22:32 ` Thomas Gleixner
2004-06-16 22:46   ` David Woodhouse
2004-06-16 22:50     ` Thomas Gleixner

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox