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