X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmtd%2Fnand%2Fdiskonchip.c;h=ec5e45e4e4efcfe9214e0076ed59c470cf2b90e7;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=0f7dedd6b88ecd95fd30d3ac761c11574478488d;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 0f7dedd6b..ec5e45e4e 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -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 * Diskonchip Millennium Plus support by Kalev Lember * + * 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 + * * 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 #include #include #include +#include +#include #include #include @@ -28,23 +35,23 @@ #include /* 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); } }