Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / mtd / nand / diskonchip.c
index 0f7dedd..ec5e45e 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
  * drivers/mtd/nand/diskonchip.c
  *
  * (C) 2003 Red Hat, Inc.
@@ -9,15 +9,22 @@
  * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org>
  * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
  *
+ * Error correction code lifted from the old docecc code
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de>
+ *
  * Interface to generic NAND code for M-Systems DiskOnChip devices
  *
- * $Id: diskonchip.c,v 1.34 2004/08/09 19:41:12 dbrown Exp $
+ * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $
  */
 
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/delay.h>
+#include <linux/rslib.h>
+#include <linux/moduleparam.h>
 #include <asm/io.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/inftl.h>
 
 /* Where to look for the devices? */
-#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS
-#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0
+#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS
+#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0
 #endif
 
 static unsigned long __initdata doc_locations[] = {
 #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
-#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH
-       0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 
+#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH
+       0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
        0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
-       0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, 
-       0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, 
+       0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
+       0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
        0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
 #else /*  CONFIG_MTD_DOCPROBE_HIGH */
-       0xc8000, 0xca000, 0xcc000, 0xce000, 
+       0xc8000, 0xca000, 0xcc000, 0xce000,
        0xd0000, 0xd2000, 0xd4000, 0xd6000,
-       0xd8000, 0xda000, 0xdc000, 0xde000, 
-       0xe0000, 0xe2000, 0xe4000, 0xe6000, 
+       0xd8000, 0xda000, 0xdc000, 0xde000,
+       0xe0000, 0xe2000, 0xe4000, 0xe6000,
        0xe8000, 0xea000, 0xec000, 0xee000,
 #endif /*  CONFIG_MTD_DOCPROBE_HIGH */
 #elif defined(__PPC__)
@@ -62,7 +69,7 @@ static unsigned long __initdata doc_locations[] = {
 static struct mtd_info *doclist = NULL;
 
 struct doc_priv {
-       unsigned long virtadr;
+       void __iomem *virtadr;
        unsigned long physadr;
        u_char ChipID;
        u_char CDSNControl;
@@ -74,11 +81,6 @@ struct doc_priv {
        struct mtd_info *nextdoc;
 };
 
-/* 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 };
@@ -96,33 +98,142 @@ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd);
 static void doc200x_select_chip(struct mtd_info *mtd, int chip);
 
 static int debug=0;
-MODULE_PARM(debug, "i");
+module_param(debug, int, 0);
 
 static int try_dword=1;
-MODULE_PARM(try_dword, "i");
+module_param(try_dword, int, 0);
 
 static int no_ecc_failures=0;
-MODULE_PARM(no_ecc_failures, "i");
+module_param(no_ecc_failures, int, 0);
 
 static int no_autopart=0;
-MODULE_PARM(no_autopart, "i");
+module_param(no_autopart, int, 0);
+
+static int show_firmware_partition=0;
+module_param(show_firmware_partition, int, 0);
 
 #ifdef MTD_NAND_DISKONCHIP_BBTWRITE
 static int inftl_bbt_write=1;
 #else
 static int inftl_bbt_write=0;
 #endif
-MODULE_PARM(inftl_bbt_write, "i");
+module_param(inftl_bbt_write, int, 0);
 
-static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS;
-MODULE_PARM(doc_config_location, "l");
+static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS;
+module_param(doc_config_location, ulong, 0);
 MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
 
+
+/* Sector size for HW ECC */
+#define SECTOR_SIZE 512
+/* The sector bytes are packed into NB_DATA 10 bit words */
+#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10)
+/* Number of roots */
+#define NROOTS 4
+/* First consective root */
+#define FCR 510
+/* Number of symbols */
+#define NN 1023
+
+/* the Reed Solomon control structure */
+static struct rs_control *rs_decoder;
+
+/*
+ * The HW decoder in the DoC ASIC's provides us a error syndrome,
+ * which we must convert to a standard syndrom usable by the generic
+ * Reed-Solomon library code.
+ *
+ * Fabrice Bellard figured this out in the old docecc code. I added
+ * some comments, improved a minor bit and converted it to make use
+ * of the generic Reed-Solomon libary. tglx
+ */
+static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
+{
+       int i, j, nerr, errpos[8];
+       uint8_t parity;
+       uint16_t ds[4], s[5], tmp, errval[8], syn[4];
+
+       /* Convert the ecc bytes into words */
+       ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8);
+       ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6);
+       ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4);
+       ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2);
+       parity = ecc[1];
+
+       /* Initialize the syndrom buffer */
+       for (i = 0; i < NROOTS; i++)
+               s[i] = ds[0];
+       /*
+        *  Evaluate
+        *  s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0]
+        *  where x = alpha^(FCR + i)
+        */
+       for(j = 1; j < NROOTS; j++) {
+               if(ds[j] == 0)
+                       continue;
+               tmp = rs->index_of[ds[j]];
+               for(i = 0; i < NROOTS; i++)
+                       s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)];
+       }
+
+       /* Calc s[i] = s[i] / alpha^(v + i) */
+       for (i = 0; i < NROOTS; i++) {
+               if (syn[i])
+                       syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i));
+       }
+       /* Call the decoder library */
+       nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval);
+
+       /* Incorrectable errors ? */
+       if (nerr < 0)
+               return nerr;
+
+       /*
+        * Correct the errors. The bitpositions are a bit of magic,
+        * but they are given by the design of the de/encoder circuit
+        * in the DoC ASIC's.
+        */
+       for(i = 0;i < nerr; i++) {
+               int index, bitpos, pos = 1015 - errpos[i];
+               uint8_t val;
+               if (pos >= NB_DATA && pos < 1019)
+                       continue;
+               if (pos < NB_DATA) {
+                       /* extract bit position (MSB first) */
+                       pos = 10 * (NB_DATA - 1 - pos) - 6;
+                       /* now correct the following 10 bits. At most two bytes
+                          can be modified since pos is even */
+                       index = (pos >> 3) ^ 1;
+                       bitpos = pos & 7;
+                       if ((index >= 0 && index < SECTOR_SIZE) ||
+                           index == (SECTOR_SIZE + 1)) {
+                               val = (uint8_t) (errval[i] >> (2 + bitpos));
+                               parity ^= val;
+                               if (index < SECTOR_SIZE)
+                                       data[index] ^= val;
+                       }
+                       index = ((pos >> 3) + 1) ^ 1;
+                       bitpos = (bitpos + 10) & 7;
+                       if (bitpos == 0)
+                               bitpos = 8;
+                       if ((index >= 0 && index < SECTOR_SIZE) ||
+                           index == (SECTOR_SIZE + 1)) {
+                               val = (uint8_t)(errval[i] << (8 - bitpos));
+                               parity ^= val;
+                               if (index < SECTOR_SIZE)
+                                       data[index] ^= val;
+                       }
+               }
+       }
+       /* If the parity is wrong, no rescue possible */
+       return parity ? -1 : nerr;
+}
+
 static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
 {
        volatile char dummy;
        int i;
-       
+
        for (i = 0; i < cycles; i++) {
                if (DoC_is_Millennium(doc))
                        dummy = ReadDOC(doc->virtadr, NOP);
@@ -131,7 +242,7 @@ static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
                else
                        dummy = ReadDOC(doc->virtadr, DOCStatus);
        }
-       
+
 }
 
 #define CDSN_CTRL_FR_B_MASK    (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)
@@ -139,7 +250,7 @@ static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
 /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
 static int _DoC_WaitReady(struct doc_priv *doc)
 {
-       unsigned long docptr = doc->virtadr;
+        void __iomem *docptr = doc->virtadr;
        unsigned long timeo = jiffies + (HZ * 10);
 
        if(debug) printk("_DoC_WaitReady...\n");
@@ -169,7 +280,7 @@ static int _DoC_WaitReady(struct doc_priv *doc)
 
 static inline int DoC_WaitReady(struct doc_priv *doc)
 {
-       unsigned long docptr = doc->virtadr;
+        void __iomem *docptr = doc->virtadr;
        int ret = 0;
 
        if (DoC_is_MillenniumPlus(doc)) {
@@ -194,8 +305,8 @@ static inline int DoC_WaitReady(struct doc_priv *doc)
 static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
 
        if(debug)printk("write_byte %02x\n", datum);
        WriteDOC(datum, docptr, CDSNSlowIO);
@@ -205,8 +316,8 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
 static u_char doc2000_read_byte(struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        u_char ret;
 
        ReadDOC(docptr, CDSNSlowIO);
@@ -216,12 +327,12 @@ static u_char doc2000_read_byte(struct mtd_info *mtd)
        return ret;
 }
 
-static void doc2000_writebuf(struct mtd_info *mtd, 
+static void doc2000_writebuf(struct mtd_info *mtd,
                             const u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
        if (debug)printk("writebuf of %d bytes: ", len);
        for (i=0; i < len; i++) {
@@ -232,12 +343,12 @@ static void doc2000_writebuf(struct mtd_info *mtd,
        if (debug) printk("\n");
 }
 
-static void doc2000_readbuf(struct mtd_info *mtd, 
+static void doc2000_readbuf(struct mtd_info *mtd,
                            u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
 
        if (debug)printk("readbuf of %d bytes: ", len);
@@ -247,12 +358,12 @@ static void doc2000_readbuf(struct mtd_info *mtd,
        }
 }
 
-static void doc2000_readbuf_dword(struct mtd_info *mtd, 
+static void doc2000_readbuf_dword(struct mtd_info *mtd,
                            u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
 
        if (debug) printk("readbuf_dword of %d bytes: ", len);
@@ -268,12 +379,12 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd,
        }
 }
 
-static int doc2000_verifybuf(struct mtd_info *mtd, 
+static int doc2000_verifybuf(struct mtd_info *mtd,
                              const u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
 
        for (i=0; i < len; i++)
@@ -285,7 +396,7 @@ static int doc2000_verifybuf(struct mtd_info *mtd,
 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;
+       struct doc_priv *doc = this->priv;
        uint16_t ret;
 
        doc200x_select_chip(mtd, nr);
@@ -296,6 +407,11 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
        this->write_byte(mtd, 0);
        doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
 
+       /* We cant' use dev_ready here, but at least we wait for the
+        * command to complete
+        */
+       udelay(50);
+
        ret = this->read_byte(mtd) << 8;
        ret |= this->read_byte(mtd);
 
@@ -305,7 +421,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
                        uint32_t dword;
                        uint8_t byte[4];
                } ident;
-               unsigned long docptr = doc->virtadr;
+               void __iomem *docptr = doc->virtadr;
 
                doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
                doc2000_write_byte(mtd, NAND_CMD_READID);
@@ -314,20 +430,22 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
                doc2000_write_byte(mtd, 0);
                doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
 
+               udelay(50);
+
                ident.dword = readl(docptr + DoC_2k_CDSN_IO);
                if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
                        printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
                        this->read_buf = &doc2000_readbuf_dword;
                }
        }
-               
+
        return ret;
 }
 
 static void __init doc2000_count_chips(struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
+       struct doc_priv *doc = this->priv;
        uint16_t mfrid;
        int i;
 
@@ -348,10 +466,10 @@ static void __init doc2000_count_chips(struct mtd_info *mtd)
 
 static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
 {
-       struct doc_priv *doc = (void *)this->priv;
+       struct doc_priv *doc = this->priv;
 
        int status;
-       
+
        DoC_WaitReady(doc);
        this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
        DoC_WaitReady(doc);
@@ -363,8 +481,8 @@ static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
 static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
 
        WriteDOC(datum, docptr, CDSNSlowIO);
        WriteDOC(datum, docptr, Mil_CDSN_IO);
@@ -374,8 +492,8 @@ static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
 static u_char doc2001_read_byte(struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
 
        //ReadDOC(docptr, CDSNSlowIO);
        /* 11.4.5 -- delay twice to allow extended length cycle */
@@ -385,12 +503,12 @@ static u_char doc2001_read_byte(struct mtd_info *mtd)
        return ReadDOC(docptr, LastDataRead);
 }
 
-static void doc2001_writebuf(struct mtd_info *mtd, 
+static void doc2001_writebuf(struct mtd_info *mtd,
                             const u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
 
        for (i=0; i < len; i++)
@@ -399,12 +517,12 @@ static void doc2001_writebuf(struct mtd_info *mtd,
        WriteDOC(0x00, docptr, WritePipeTerm);
 }
 
-static void doc2001_readbuf(struct mtd_info *mtd, 
+static void doc2001_readbuf(struct mtd_info *mtd,
                            u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
 
        /* Start read pipeline */
@@ -417,12 +535,12 @@ static void doc2001_readbuf(struct mtd_info *mtd,
        buf[i] = ReadDOC(docptr, LastDataRead);
 }
 
-static int doc2001_verifybuf(struct mtd_info *mtd, 
+static int doc2001_verifybuf(struct mtd_info *mtd,
                             const u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
 
        /* Start read pipeline */
@@ -441,8 +559,8 @@ static int doc2001_verifybuf(struct mtd_info *mtd,
 static u_char doc2001plus_read_byte(struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        u_char ret;
 
         ReadDOC(docptr, Mplus_ReadPipeInit);
@@ -452,12 +570,12 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd)
        return ret;
 }
 
-static void doc2001plus_writebuf(struct mtd_info *mtd, 
+static void doc2001plus_writebuf(struct mtd_info *mtd,
                             const u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
 
        if (debug)printk("writebuf of %d bytes: ", len);
@@ -469,12 +587,12 @@ static void doc2001plus_writebuf(struct mtd_info *mtd,
        if (debug) printk("\n");
 }
 
-static void doc2001plus_readbuf(struct mtd_info *mtd, 
+static void doc2001plus_readbuf(struct mtd_info *mtd,
                            u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
 
        if (debug)printk("readbuf of %d bytes: ", len);
@@ -499,12 +617,12 @@ static void doc2001plus_readbuf(struct mtd_info *mtd,
        if (debug) printk("\n");
 }
 
-static int doc2001plus_verifybuf(struct mtd_info *mtd, 
+static int doc2001plus_verifybuf(struct mtd_info *mtd,
                             const u_char *buf, int len)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
 
        if (debug)printk("verifybuf of %d bytes: ", len);
@@ -529,8 +647,8 @@ static int doc2001plus_verifybuf(struct mtd_info *mtd,
 static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int floor = 0;
 
        if(debug)printk("select chip (%d)\n", chip);
@@ -555,8 +673,8 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
 static void doc200x_select_chip(struct mtd_info *mtd, int chip)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int floor = 0;
 
        if(debug)printk("select chip (%d)\n", chip);
@@ -582,8 +700,8 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip)
 static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
 
        switch(cmd) {
        case NAND_CTL_SETNCE:
@@ -620,8 +738,8 @@ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
 static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
 
        /*
         * Must terminate write pipeline before sending any commands
@@ -679,7 +797,7 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
                        WriteDOC(0, docptr, Mplus_FlashControl);
        }
 
-       /* 
+       /*
         * program and erase have their own busy handlers
         * status and sequential in needs no delay
        */
@@ -704,7 +822,7 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
 
        /* This applies to read commands */
        default:
-               /* 
+               /*
                 * If we don't have access to the busy pin, we apply the given
                 * command delay
                */
@@ -724,8 +842,8 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
 static int doc200x_dev_ready(struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
 
        if (DoC_is_MillenniumPlus(doc)) {
                /* 11.4.2 -- must NOP four times before checking FR/B# */
@@ -762,8 +880,8 @@ static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 static void 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;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
 
        /* Prime the ECC engine */
        switch(mode) {
@@ -781,8 +899,8 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
 static void doc2001plus_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;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
 
        /* Prime the ECC engine */
        switch(mode) {
@@ -802,8 +920,8 @@ static int doc200x_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;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        int i;
        int emptymatch = 1;
 
@@ -827,7 +945,7 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
        for (i = 0; i < 6; i++) {
                if (DoC_is_MillenniumPlus(doc))
                        ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
-               else 
+               else
                        ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
                if (ecc_code[i] != empty_write_ecc[i])
                        emptymatch = 0;
@@ -860,11 +978,11 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
 {
        int i, ret = 0;
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
-       unsigned long docptr = doc->virtadr;
+       struct doc_priv *doc = this->priv;
+        void __iomem *docptr = doc->virtadr;
        volatile u_char dummy;
        int emptymatch = 1;
-       
+
        /* flush the pipeline */
        if (DoC_is_2000(doc)) {
                dummy = ReadDOC(docptr, 2k_ECCStatus);
@@ -879,7 +997,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
                dummy = ReadDOC(docptr, ECCConf);
                dummy = ReadDOC(docptr, ECCConf);
        }
-       
+
        /* Error occured ? */
        if (dummy & 0x80) {
                for (i = 0; i < 6; i++) {
@@ -914,10 +1032,10 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
                   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. */
-               if (!emptymatch) ret = doc_decode_ecc (dat, calc_ecc);
+               if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc);
                if (ret > 0)
                        printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
-       }       
+       }
        if (DoC_is_MillenniumPlus(doc))
                WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
        else
@@ -928,16 +1046,26 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
        }
        return ret;
 }
-               
+
 //u_char mydatabuf[528];
 
+/* The strange out-of-order .oobfree list below is a (possibly unneeded)
+ * attempt to retain compatibility.  It used to read:
+ *     .oobfree = { {8, 8} }
+ * Since that leaves two bytes unusable, it was changed.  But the following
+ * scheme might affect existing jffs2 installs by moving the cleanmarker:
+ *     .oobfree = { {6, 10} }
+ * jffs2 seems to handle the above gracefully, but the current scheme seems
+ * safer.  The only problem with it is that any code that parses oobfree must
+ * be able to handle out-of-order segments.
+ */
 static struct nand_oobinfo doc200x_oobinfo = {
         .useecc = MTD_NANDECC_AUTOPLACE,
         .eccbytes = 6,
         .eccpos = {0, 1, 2, 3, 4, 5},
-        .oobfree = { {8, 8} }
+        .oobfree = { {8, 8}, {6, 2} }
 };
+
 /* 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
@@ -948,13 +1076,12 @@ 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;
-       unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift);
+       struct doc_priv *doc = this->priv;
+       unsigned offs;
        int ret;
        size_t retlen;
 
-       end = min(end, mtd->size); // paranoia
-       for (offs = 0; offs < end; offs += mtd->erasesize) {
+       for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
                ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
                if (retlen != mtd->oobblock) continue;
                if (ret) {
@@ -991,11 +1118,12 @@ static inline 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;
+       struct doc_priv *doc = this->priv;
        int ret = 0;
        u_char *buf;
        struct NFTLMediaHeader *mh;
        const unsigned psize = 1 << this->page_shift;
+       int numparts = 0;
        unsigned blocks, maxblocks;
        int offs, numheaders;
 
@@ -1007,8 +1135,10 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
        if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out;
        mh = (struct NFTLMediaHeader *) buf;
 
-//#ifdef CONFIG_MTD_DEBUG_VERBOSE
-//     if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+       mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits);
+       mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN);
+       mh->FormattedSize = le32_to_cpu(mh->FormattedSize);
+
        printk(KERN_INFO "    DataOrgID        = %s\n"
                         "    NumEraseUnits    = %d\n"
                         "    FirstPhysicalEUN = %d\n"
@@ -1017,7 +1147,6 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
                mh->DataOrgID, mh->NumEraseUnits,
                mh->FirstPhysicalEUN, mh->FormattedSize,
                mh->UnitSizeFactor);
-//#endif
 
        blocks = mtd->size >> this->phys_erase_shift;
        maxblocks = min(32768U, mtd->erasesize - psize);
@@ -1060,23 +1189,28 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
        offs <<= this->page_shift;
        offs += mtd->erasesize;
 
-       //parts[0].name = " DiskOnChip Boot / Media Header partition";
-       //parts[0].offset = 0;
-       //parts[0].size = offs;
+       if (show_firmware_partition == 1) {
+               parts[0].name = " DiskOnChip Firmware / Media Header partition";
+               parts[0].offset = 0;
+               parts[0].size = offs;
+               numparts = 1;
+       }
+
+       parts[numparts].name = " DiskOnChip BDTL partition";
+       parts[numparts].offset = offs;
+       parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
 
-       parts[0].name = " DiskOnChip BDTL partition";
-       parts[0].offset = offs;
-       parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
+       offs += parts[numparts].size;
+       numparts++;
 
-       offs += parts[0].size;
        if (offs < mtd->size) {
-               parts[1].name = " DiskOnChip Remainder partition";
-               parts[1].offset = offs;
-               parts[1].size = mtd->size - offs;
-               ret = 2;
-               goto out;
+               parts[numparts].name = " DiskOnChip Remainder partition";
+               parts[numparts].offset = offs;
+               parts[numparts].size = mtd->size - offs;
+               numparts++;
        }
-       ret = 1;
+
+       ret = numparts;
 out:
        kfree(buf);
        return ret;
@@ -1087,7 +1221,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
                                 struct mtd_partition *parts)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
+       struct doc_priv *doc = this->priv;
        int ret = 0;
        u_char *buf;
        struct INFTLMediaHeader *mh;
@@ -1117,9 +1251,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
        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 "    bootRecordID          = %s\n"
                         "    NoOfBootImageBlocks   = %d\n"
                         "    NoOfBinaryPartitions  = %d\n"
@@ -1137,7 +1269,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
                ((unsigned char *) &mh->OsakVersion)[2] & 0xf,
                ((unsigned char *) &mh->OsakVersion)[3] & 0xf,
                mh->PercentUsed);
-//#endif
 
        vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
 
@@ -1163,8 +1294,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
                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"
@@ -1174,16 +1303,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
                        i, ip->virtualUnits, ip->firstUnit,
                        ip->lastUnit, ip->flags,
                        ip->spareUnits);
-//#endif
 
-/*
-               if ((i == 0) && (ip->firstUnit > 0)) {
+               if ((show_firmware_partition == 1) &&
+                   (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";
@@ -1212,7 +1339,7 @@ static 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 doc_priv *doc = this->priv;
        struct mtd_partition parts[2];
 
        memset((char *) parts, 0, sizeof(parts));
@@ -1251,7 +1378,7 @@ static 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 doc_priv *doc = this->priv;
        struct mtd_partition parts[5];
 
        if (this->numchips > doc->chips_per_floor) {
@@ -1310,7 +1437,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
 static inline int __init doc2000_init(struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
+       struct doc_priv *doc = this->priv;
 
        this->write_byte = doc2000_write_byte;
        this->read_byte = doc2000_read_byte;
@@ -1328,7 +1455,7 @@ static inline int __init doc2000_init(struct mtd_info *mtd)
 static inline int __init doc2001_init(struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
+       struct doc_priv *doc = this->priv;
 
        this->write_byte = doc2001_write_byte;
        this->read_byte = doc2001_read_byte;
@@ -1341,7 +1468,7 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
        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. 
+                  DiskOnChip 2000 units with a similar ASIC.
                   Treat it like a Millennium, except that it
                   can have multiple chips. */
                doc2000_count_chips(mtd);
@@ -1360,7 +1487,7 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
 static inline int __init doc2001plus_init(struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
-       struct doc_priv *doc = (void *)this->priv;
+       struct doc_priv *doc = this->priv;
 
        this->write_byte = NULL;
        this->read_byte = doc2001plus_read_byte;
@@ -1379,19 +1506,19 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
        return 1;
 }
 
-static inline int __init doc_probe(unsigned long physadr)
+static 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;
+       void __iomem *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);
+       virtadr = 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;
@@ -1403,20 +1530,20 @@ static inline int __init doc_probe(unsigned long physadr)
         * 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. 
+        * found it.
         */
        save_control = ReadDOC(virtadr, DOCControl);
 
        /* Reset the DiskOnChip ASIC */
-       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, 
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
                 virtadr, DOCControl);
-       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, 
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
                 virtadr, DOCControl);
 
        /* Enable the DiskOnChip ASIC */
-       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, 
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
                 virtadr, DOCControl);
-       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, 
+       WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
                 virtadr, DOCControl);
 
        ChipID = ReadDOC(virtadr, ChipID);
@@ -1482,7 +1609,7 @@ static inline int __init doc_probe(unsigned long physadr)
                unsigned char oldval;
                unsigned char newval;
                nand = mtd->priv;
-               doc = (void *)nand->priv;
+               doc = 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
@@ -1518,7 +1645,7 @@ static inline int __init doc_probe(unsigned long physadr)
              sizeof(struct nand_chip) +
              sizeof(struct doc_priv) +
              (2 * sizeof(struct nand_bbt_descr));
-       mtd = kmalloc(len, GFP_KERNEL);
+       mtd =  kmalloc(len, GFP_KERNEL);
        if (!mtd) {
                printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
                ret = -ENOMEM;
@@ -1531,10 +1658,10 @@ static inline int __init doc_probe(unsigned long physadr)
        nand->bbt_td            = (struct nand_bbt_descr *) (doc + 1);
        nand->bbt_md            = nand->bbt_td + 1;
 
-       mtd->priv               = (void *) nand;
+       mtd->priv               = nand;
        mtd->owner              = THIS_MODULE;
 
-       nand->priv              = (void *) doc;
+       nand->priv              = doc;
        nand->select_chip       = doc200x_select_chip;
        nand->hwcontrol         = doc200x_hwcontrol;
        nand->dev_ready         = doc200x_dev_ready;
@@ -1543,7 +1670,7 @@ static inline int __init doc_probe(unsigned long physadr)
        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;
@@ -1585,17 +1712,51 @@ notfound:
           actually a DiskOnChip.  */
        WriteDOC(save_control, virtadr, DOCControl);
 fail:
-       iounmap((void *)virtadr);
+       iounmap(virtadr);
        return ret;
 }
 
-int __init init_nanddoc(void)
+static void release_nanddoc(void)
 {
-       int i;
+       struct mtd_info *mtd, *nextmtd;
+       struct nand_chip *nand;
+       struct doc_priv *doc;
+
+       for (mtd = doclist; mtd; mtd = nextmtd) {
+               nand = mtd->priv;
+               doc = nand->priv;
+
+               nextmtd = doc->nextdoc;
+               nand_release(mtd);
+               iounmap(doc->virtadr);
+               kfree(mtd);
+       }
+}
+
+static int __init init_nanddoc(void)
+{
+       int i, ret = 0;
+
+       /* We could create the decoder on demand, if memory is a concern.
+        * This way we have it handy, if an error happens
+        *
+        * Symbolsize is 10 (bits)
+        * Primitve polynomial is x^10+x^3+1
+        * first consecutive root is 510
+        * primitve element to generate roots = 1
+        * generator polinomial degree = 4
+        */
+       rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS);
+       if (!rs_decoder) {
+               printk (KERN_ERR "DiskOnChip: Could not create a RS decoder\n");
+               return -ENOMEM;
+       }
 
        if (doc_config_location) {
                printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
-               return doc_probe(doc_config_location);
+               ret = doc_probe(doc_config_location);
+               if (ret < 0)
+                       goto outerr;
        } else {
                for (i=0; (doc_locations[i] != 0xffffffff); i++) {
                        doc_probe(doc_locations[i]);
@@ -1605,25 +1766,23 @@ int __init init_nanddoc(void)
           found, so the user knows we at least tried. */
        if (!doclist) {
                printk(KERN_INFO "No valid DiskOnChip devices found\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto outerr;
        }
        return 0;
+outerr:
+       free_rs(rs_decoder);
+       return ret;
 }
 
-void __exit cleanup_nanddoc(void)
+static void __exit cleanup_nanddoc(void)
 {
-       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;
+       /* Cleanup the nand/DoC resources */
+       release_nanddoc();
 
-               nextmtd = doc->nextdoc;
-               nand_release(mtd);
-               iounmap((void *)doc->virtadr);
-               kfree(mtd);
+       /* Free the reed solomon resources */
+       if (rs_decoder) {
+               free_rs(rs_decoder);
        }
 }