mtd/drivers/mtd/nand diskonchip.c,1.18,1.19
dbrown at infradead.org
dbrown at infradead.org
Fri Jun 25 10:39:02 EDT 2004
Update of /home/cvs/mtd/drivers/mtd/nand
In directory phoenix.infradead.org:/tmp/cvs-serv18502
Modified Files:
diskonchip.c
Log Message:
Add proper probing and multiple device support.
Index: diskonchip.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/nand/diskonchip.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -r1.18 -r1.19
--- diskonchip.c 24 Jun 2004 15:02:50 -0000 1.18
+++ diskonchip.c 25 Jun 2004 14:38:59 -0000 1.19
@@ -23,6 +23,40 @@
#include <linux/mtd/partitions.h>
#include <linux/mtd/inftl.h>
+/* Where to look for the devices? */
+#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
+#define CONFIG_MTD_DOCPROBE_ADDRESS 0
+#endif
+
+static unsigned long __initdata doc_locations[] = {
+#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
+#ifdef CONFIG_MTD_DOCPROBE_HIGH
+ 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
+ 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
+ 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
+ 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
+ 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
+#else /* CONFIG_MTD_DOCPROBE_HIGH */
+ 0xc8000, 0xca000, 0xcc000, 0xce000,
+ 0xd0000, 0xd2000, 0xd4000, 0xd6000,
+ 0xd8000, 0xda000, 0xdc000, 0xde000,
+ 0xe0000, 0xe2000, 0xe4000, 0xe6000,
+ 0xe8000, 0xea000, 0xec000, 0xee000,
+#endif /* CONFIG_MTD_DOCPROBE_HIGH */
+#elif defined(__PPC__)
+ 0xe4000000,
+#elif defined(CONFIG_MOMENCO_OCELOT)
+ 0x2f000000,
+ 0xff000000,
+#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
+ 0xff000000,
+##else
+#warning Unknown architecture for DiskOnChip. No default probe locations defined
+#endif
+ 0xffffffff };
+
+static struct mtd_info *doclist = NULL;
+
struct doc_priv {
unsigned long virtadr;
unsigned long physadr;
@@ -33,6 +67,7 @@
int curchip;
int mh0_page;
int mh1_page;
+ struct mtd_info *nextdoc;
};
/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL
@@ -49,36 +84,6 @@
#define INFTL_BBT_RESERVED_BLOCKS 4
-static struct nand_bbt_descr inftl_bbt_descr0 = {
- .options =NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION,
- .offs =8,
- .len = 8,
- .veroffs = 6,
- .maxblocks = INFTL_BBT_RESERVED_BLOCKS,
- .reserved_block_code = 0x01,
- .pattern = "MSYS_BBT"
-};
-
-static struct nand_bbt_descr inftl_bbt_descr1 = {
- .options =NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION,
- .offs =8,
- .len = 8,
- .veroffs = 6,
- .maxblocks = INFTL_BBT_RESERVED_BLOCKS,
- .reserved_block_code = 0x01,
- .pattern = "TBB_SYSM"
-};
-
-static struct nand_bbt_descr nftl_bbt_descr0 = {
- .options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | NAND_BBT_VERSION,
- .veroffs = 6
-};
-
-static struct nand_bbt_descr nftl_bbt_descr1 = {
- .options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | NAND_BBT_VERSION,
- .veroffs = 6
-};
-
#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
@@ -100,6 +105,10 @@
static int inftl_bbt_write=0;
MODULE_PARM(inftl_bbt_write, "i");
+static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
+MODULE_PARM(doc_config_location, "l");
+MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
+
static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
{
volatile char dummy;
@@ -480,13 +489,14 @@
return 1;
}
-static int doc200x_block_bad(struct mtd_info *mtd, unsigned long block)
+static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
- /* FIXME: Look it up in the BBT */
+ /* This is our last resort if we couldn't find or create a BBT. Just
+ pretend all blocks are good. */
return 0;
}
-static int doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
+static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
@@ -503,7 +513,6 @@
WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
break;
}
- return 0;
}
/* This code is only called on write */
@@ -618,12 +627,6 @@
return ret;
}
-static struct doc_priv mydoc = {
- .physadr = 0xd0000,
- .curfloor = -1,
- .curchip = -1,
-};
-
//u_char mydatabuf[528];
static struct nand_oobinfo doc200x_oobinfo = {
@@ -633,27 +636,6 @@
.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_HW6_512,
- //.data_buf = mydatabuf,
- .options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME,
- .autooob = &doc200x_oobinfo,
- .correct_data = doc200x_correct_data,
- .enable_hwecc = doc200x_enable_hwecc,
- .calculate_ecc = doc200x_calculate_ecc
-};
-
-static struct mtd_info mymtd = {
- .priv = (void *)&mynand,
- .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
@@ -702,7 +684,7 @@
return 1;
}
-static int __init nftl_partscan(struct mtd_info *mtd,
+static inline int __init nftl_partscan(struct mtd_info *mtd,
struct mtd_partition *parts)
{
struct nand_chip *this = mtd->priv;
@@ -768,17 +750,17 @@
offs <<= this->page_shift;
offs += mtd->erasesize;
- //parts[0].name = "DiskOnChip Boot / Media Header partition";
+ //parts[0].name = " DiskOnChip Boot / Media Header partition";
//parts[0].offset = 0;
//parts[0].size = offs;
- parts[0].name = "DiskOnChip BDTL partition";
+ parts[0].name = " DiskOnChip BDTL partition";
parts[0].offset = offs;
parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
offs += parts[0].size;
if (offs < mtd->size) {
- parts[1].name = "DiskOnChip Remainder partition";
+ parts[1].name = " DiskOnChip Remainder partition";
parts[1].offset = offs;
parts[1].size = mtd->size - offs;
return 2;
@@ -787,7 +769,7 @@
}
/* This is a stripped-down copy of the code in inftlmount.c */
-static int __init inftl_partscan(struct mtd_info *mtd,
+static inline int __init inftl_partscan(struct mtd_info *mtd,
struct mtd_partition *parts)
{
struct nand_chip *this = mtd->priv;
@@ -870,7 +852,7 @@
/*
if ((i == 0) && (ip->firstUnit > 0)) {
- parts[0].name = "DiskOnChip IPL / Media Header partition";
+ parts[0].name = " DiskOnChip IPL / Media Header partition";
parts[0].offset = 0;
parts[0].size = mtd->erasesize * ip->firstUnit;
numparts = 1;
@@ -878,9 +860,9 @@
*/
if (ip->flags & INFTL_BINARY)
- parts[numparts].name = "DiskOnChip BDK partition";
+ parts[numparts].name = " DiskOnChip BDK partition";
else
- parts[numparts].name = "DiskOnChip BDTL partition";
+ parts[numparts].name = " DiskOnChip BDTL partition";
parts[numparts].offset = ip->firstUnit << vshift;
parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
numparts++;
@@ -889,7 +871,7 @@
}
lastvunit++;
if ((lastvunit << vshift) < end) {
- parts[numparts].name = "DiskOnChip Remainder partition";
+ parts[numparts].name = " DiskOnChip Remainder partition";
parts[numparts].offset = lastvunit << vshift;
parts[numparts].size = end - parts[numparts].offset;
numparts++;
@@ -897,7 +879,7 @@
return numparts;
}
-int __init nftl_scan_bbt(struct mtd_info *mtd)
+static int __init nftl_scan_bbt(struct mtd_info *mtd)
{
int ret, numparts;
struct nand_chip *this = mtd->priv;
@@ -909,14 +891,25 @@
BBTs, since they're stored in the media header eraseblocks. */
numparts = nftl_partscan(mtd, parts);
if (!numparts) return -EIO;
+ this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+ NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+ NAND_BBT_VERSION;
+ this->bbt_td->veroffs = 6;
this->bbt_td->pages[0] = doc->mh0_page + 1;
- if (doc->mh1_page == -1)
- this->bbt_md = NULL;
- else
+ if (doc->mh1_page != -1) {
+ this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+ NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+ NAND_BBT_VERSION;
+ this->bbt_md->veroffs = 6;
this->bbt_md->pages[0] = doc->mh1_page + 1;
+ } else {
+ this->bbt_md = NULL;
+ }
+
/* 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;
+ if ((ret = nand_scan_bbt(mtd, NULL)))
+ return ret;
add_mtd_device(mtd);
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);
@@ -924,7 +917,7 @@
return 0;
}
-int __init inftl_scan_bbt(struct mtd_info *mtd)
+static int __init inftl_scan_bbt(struct mtd_info *mtd)
{
int ret, numparts;
struct nand_chip *this = mtd->priv;
@@ -935,13 +928,33 @@
printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n");
return -EIO;
}
- if (inftl_bbt_write) {
+
+ this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+ NAND_BBT_VERSION;
+ if (inftl_bbt_write)
this->bbt_td->options |= NAND_BBT_WRITE;
+ this->bbt_td->offs = 8;
+ this->bbt_td->len = 8;
+ this->bbt_td->veroffs = 6;
+ this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+ this->bbt_td->reserved_block_code = 0x01;
+ this->bbt_td->pattern = "MSYS_BBT";
+
+ this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+ NAND_BBT_VERSION;
+ if (inftl_bbt_write)
this->bbt_md->options |= NAND_BBT_WRITE;
- }
+ this->bbt_md->offs = 8;
+ this->bbt_md->len = 8;
+ this->bbt_md->veroffs = 6;
+ this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+ this->bbt_md->reserved_block_code = 0x01;
+ this->bbt_md->pattern = "TBB_SYSM";
+
/* 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;
+ 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. We could probably
@@ -955,92 +968,250 @@
return 0;
}
-int __init init_nanddoc(void)
+static inline int __init doc2000_init(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+
+ this->write_byte = doc2000_write_byte;
+ this->read_byte = doc2000_read_byte;
+ this->write_buf = doc2000_writebuf;
+ this->read_buf = doc2000_readbuf;
+ this->verify_buf = doc2000_verifybuf;
+ this->scan_bbt = nftl_scan_bbt;
+
+ doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
+ doc2000_count_chips(mtd);
+ mtd->name = "DiskOnChip 2000 (NFTL Model)";
+ return (4 * doc->chips_per_floor);
+}
+
+static inline int __init doc2001_init(struct mtd_info *mtd)
{
- int ret;
- int nrchips = 1;
- mydoc.virtadr = (unsigned long)ioremap(mydoc.physadr, DOC_IOREMAP_LEN);
- mydoc.mh0_page = mydoc.mh1_page = -1;
+ struct nand_chip *this = mtd->priv;
+ struct doc_priv *doc = (void *)this->priv;
+ this->write_byte = doc2001_write_byte;
+ this->read_byte = doc2001_read_byte;
+ this->write_buf = doc2001_writebuf;
+ this->read_buf = doc2001_readbuf;
+ this->verify_buf = doc2001_verifybuf;
+ this->scan_bbt = inftl_scan_bbt;
+
+ ReadDOC(doc->virtadr, ChipID);
+ ReadDOC(doc->virtadr, ChipID);
+ ReadDOC(doc->virtadr, ChipID);
+ if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) {
+ /* It's not a Millennium; it's one of the newer
+ DiskOnChip 2000 units with a similar ASIC.
+ Treat it like a Millennium, except that it
+ can have multiple chips. */
+ doc2000_count_chips(mtd);
+ mtd->name = "DiskOnChip 2000 (INFTL Model)";
+ return (4 * doc->chips_per_floor);
+ } else {
+ /* Bog-standard Millennium */
+ doc->chips_per_floor = 1;
+ mtd->name = "DiskOnChip Millennium";
+ return 1;
+ }
+}
+
+static inline int __init doc_probe(unsigned long physadr)
+{
+ unsigned char ChipID;
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ struct doc_priv *doc;
+ unsigned long virtadr;
+ unsigned char save_control;
+ unsigned char tmp, tmpb, tmpc;
+ int reg, len, numchips;
+ int ret = 0;
+
+ virtadr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN);
+ if (!virtadr) {
+ printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr);
+ return -EIO;
+ }
+
+ /* It's not possible to cleanly detect the DiskOnChip - the
+ * bootup procedure will put the device into reset mode, and
+ * it's not possible to talk to it without actually writing
+ * to the DOCControl register. So we store the current contents
+ * of the DOCControl register's location, in case we later decide
+ * that it's not a DiskOnChip, and want to put it back how we
+ * found it.
+ */
+ save_control = ReadDOC(virtadr, DOCControl);
+
+ /* Reset the DiskOnChip ASIC */
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
- mydoc.virtadr, DOCControl);
+ virtadr, DOCControl);
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
- mydoc.virtadr, DOCControl);
+ virtadr, DOCControl);
+ /* Enable the DiskOnChip ASIC */
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
- mydoc.virtadr, DOCControl);
+ virtadr, DOCControl);
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
- mydoc.virtadr, DOCControl);
+ virtadr, DOCControl);
- mydoc.ChipID = ReadDOC(mydoc.virtadr, ChipID);
+ ChipID = ReadDOC(virtadr, ChipID);
- switch(mydoc.ChipID) {
+ switch(ChipID) {
+ case DOC_ChipID_Doc2k:
+ reg = DoC_2k_ECCStatus;
+ break;
case DOC_ChipID_DocMil:
- mynand.write_byte = doc2001_write_byte;
- mynand.read_byte = doc2001_read_byte;
- mynand.write_buf = doc2001_writebuf;
- mynand.read_buf = doc2001_readbuf;
- mynand.verify_buf = doc2001_verifybuf;
- 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);
- if (ReadDOC(mydoc.virtadr, ChipID) != DOC_ChipID_DocMil) {
- /* It's not a Millennium; it's one of the newer
- DiskOnChip 2000 units with a similar ASIC.
- Treat it like a Millennium, except that it
- can have multiple chips. */
- doc2000_count_chips(&mymtd);
- nrchips = 4 * mydoc.chips_per_floor;
- mymtd.name = "DiskOnChip 2000 (INFTL Model)";
- } else {
- /* Bog-standard Millennium */
- mydoc.chips_per_floor = 1;
- nrchips = 1;
- mymtd.name = "DiskOnChip Millennium";
- }
+ reg = DoC_ECCConf;
break;
+ default:
+ ret = -ENODEV;
+ goto notfound;
+ }
+ /* Check the TOGGLE bit in the ECC register */
+ tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+ tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+ tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+ if ((tmp == tmpb) || (tmp != tmpc)) {
+ printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
+ ret = -ENODEV;
+ goto notfound;
+ }
+
+ for (mtd = doclist; mtd; mtd = doc->nextdoc) {
+ nand = mtd->priv;
+ doc = (void *)nand->priv;
+ /* Use the alias resolution register to determine if this is
+ in fact the same DOC aliased to a new address. If writes
+ to one chip's alias resolution register change the value on
+ the other chip, they're the same chip. */
+ unsigned char oldval = ReadDOC(doc->virtadr, AliasResolution);
+ unsigned char newval = ReadDOC(virtadr, AliasResolution);
+ if (oldval != newval)
+ continue;
+ WriteDOC(~newval, virtadr, AliasResolution);
+ oldval = ReadDOC(doc->virtadr, AliasResolution);
+ WriteDOC(newval, virtadr, AliasResolution); // restore it
+ newval = ~newval;
+ if (oldval == newval) {
+ //printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr);
+ goto notfound;
+ }
+ }
- case DOC_ChipID_Doc2k:
- mynand.write_byte = doc2000_write_byte;
- mynand.read_byte = doc2000_read_byte;
- 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;
-
- mydoc.CDSNControl |= CDSN_CTRL_FLASH_IO|CDSN_CTRL_ECC_IO;
- doc2000_count_chips(&mymtd);
- nrchips = 4 * mydoc.chips_per_floor;
- mymtd.name = "DiskOnChip 2000 (NFTL Model)";
+ printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
- break;
+ len = sizeof(struct mtd_info) +
+ sizeof(struct nand_chip) +
+ sizeof(struct doc_priv) +
+ (2 * sizeof(struct nand_bbt_descr));
+ mtd = kmalloc(len, GFP_KERNEL);
+ if (!mtd) {
+ printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
+ ret = -ENOMEM;
+ goto fail;
+ }
+ memset(mtd, 0, len);
+
+ nand = (struct nand_chip *) (mtd + 1);
+ doc = (struct doc_priv *) (nand + 1);
+ nand->bbt_td = (struct nand_bbt_descr *) (doc + 1);
+ nand->bbt_md = nand->bbt_td + 1;
+
+ mtd->priv = (void *) nand;
+ mtd->owner = THIS_MODULE;
+
+ nand->priv = (void *) doc;
+ nand->select_chip = doc200x_select_chip;
+ nand->hwcontrol = doc200x_hwcontrol;
+ nand->dev_ready = doc200x_dev_ready;
+ nand->waitfunc = doc200x_wait;
+ nand->block_bad = doc200x_block_bad;
+ nand->enable_hwecc = doc200x_enable_hwecc;
+ nand->calculate_ecc = doc200x_calculate_ecc;
+ nand->correct_data = doc200x_correct_data;
+ //nand->data_buf
+ nand->autooob = &doc200x_oobinfo;
+ nand->eccmode = NAND_ECC_HW6_512;
+ nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME;
+
+ doc->physadr = physadr;
+ doc->virtadr = virtadr;
+ doc->ChipID = ChipID;
+ doc->curfloor = -1;
+ doc->curchip = -1;
+ doc->mh0_page = -1;
+ doc->mh1_page = -1;
+ doc->nextdoc = doclist;
- default:
- return -EIO;
- }
- if (ret = nand_scan(&mymtd, nrchips)) {
+ if (ChipID == DOC_ChipID_Doc2k)
+ numchips = doc2000_init(mtd);
+ else
+ numchips = doc2001_init(mtd);
+
+ if ((ret = nand_scan(mtd, numchips))) {
/* 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);
+ nand_release(mtd);
+ kfree(mtd);
+ goto fail;
}
+ /* Success! */
+ doclist = mtd;
+ return 0;
+
+notfound:
+ /* Put back the contents of the DOCControl register, in case it's not
+ actually a DiskOnChip. */
+ WriteDOC(save_control, virtadr, DOCControl);
+fail:
+ iounmap((void *)virtadr);
return ret;
}
+int __init init_nanddoc(void)
+{
+ int i;
+
+ if (doc_config_location) {
+ printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
+ return doc_probe(doc_config_location);
+ } else {
+ for (i=0; (doc_locations[i] != 0xffffffff); i++) {
+ doc_probe(doc_locations[i]);
+ }
+ }
+ /* No banner message any more. Print a message if no DiskOnChip
+ found, so the user knows we at least tried. */
+ if (!doclist) {
+ printk(KERN_INFO "No valid DiskOnChip devices found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
void __exit cleanup_nanddoc(void)
{
- nand_release(&mymtd);
- iounmap((void *)mydoc.virtadr);
+ struct mtd_info *mtd, *nextmtd;
+ struct nand_chip *nand;
+ struct doc_priv *doc;
+
+ for (mtd = doclist; mtd; mtd = nextmtd) {
+ nand = mtd->priv;
+ doc = (void *)nand->priv;
+
+ nextmtd = doc->nextdoc;
+ nand_release(mtd);
+ iounmap((void *)doc->virtadr);
+ kfree(mtd);
+ }
}
module_init(init_nanddoc);
More information about the linux-mtd-cvs
mailing list