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