diff options
Diffstat (limited to 'drivers/mtd/nand/docg4.c')
| -rw-r--r-- | drivers/mtd/nand/docg4.c | 118 |
1 files changed, 76 insertions, 42 deletions
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 799da5d1c85..ce24637e14f 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -44,6 +44,26 @@ #include <linux/mtd/nand.h> #include <linux/bch.h> #include <linux/bitrev.h> +#include <linux/jiffies.h> + +/* + * In "reliable mode" consecutive 2k pages are used in parallel (in some + * fashion) to store the same data. The data can be read back from the + * even-numbered pages in the normal manner; odd-numbered pages will appear to + * contain junk. Systems that boot from the docg4 typically write the secondary + * program loader (SPL) code in this mode. The SPL is loaded by the initial + * program loader (IPL, stored in the docg4's 2k NOR-like region that is mapped + * to the reset vector address). This module parameter enables you to use this + * driver to write the SPL. When in this mode, no more than 2k of data can be + * written at a time, because the addresses do not increment in the normal + * manner, and the starting offset must be within an even-numbered 2k region; + * i.e., invalid starting offsets are 0x800, 0xa00, 0xc00, 0xe00, 0x1800, + * 0x1a00, ... Reliable mode is a special case and should not be used unless + * you know what you're doing. + */ +static bool reliable_mode; +module_param(reliable_mode, bool, 0); +MODULE_PARM_DESC(reliable_mode, "pages are programmed in reliable mode"); /* * You'll want to ignore badblocks if you're reading a partition that contains @@ -113,6 +133,7 @@ struct docg4_priv { #define DOCG4_SEQ_PAGEWRITE 0x16 #define DOCG4_SEQ_PAGEPROG 0x1e #define DOCG4_SEQ_BLOCKERASE 0x24 +#define DOCG4_SEQ_SETMODE 0x45 /* DOC_FLASHCOMMAND register commands */ #define DOCG4_CMD_PAGE_READ 0x00 @@ -122,6 +143,8 @@ struct docg4_priv { #define DOC_CMD_PROG_BLOCK_ADDR 0x60 #define DOCG4_CMD_PAGEWRITE 0x80 #define DOC_CMD_PROG_CYCLE2 0x10 +#define DOCG4_CMD_FAST_MODE 0xa3 /* functionality guessed */ +#define DOC_CMD_RELIABLE_MODE 0x22 #define DOC_CMD_RESET 0xff /* DOC_POWERMODE register bits */ @@ -190,17 +213,20 @@ struct docg4_priv { #define DOCG4_T 4 /* BCH alg corrects up to 4 bit errors */ #define DOCG4_FACTORY_BBT_PAGE 16 /* page where read-only factory bbt lives */ +#define DOCG4_REDUNDANT_BBT_PAGE 24 /* page where redundant factory bbt lives */ /* - * Oob bytes 0 - 6 are available to the user. - * Byte 7 is hamming ecc for first 7 bytes. Bytes 8 - 14 are hw-generated ecc. + * Bytes 0, 1 are used as badblock marker. + * Bytes 2 - 6 are available to the user. + * Byte 7 is hamming ecc for first 7 oob bytes only. + * Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14. * Byte 15 (the last) is used by the driver as a "page written" flag. */ static struct nand_ecclayout docg4_oobinfo = { .eccbytes = 9, .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, - .oobavail = 7, - .oobfree = { {0, 7} } + .oobavail = 5, + .oobfree = { {.offset = 2, .length = 5} } }; /* @@ -244,7 +270,7 @@ static int poll_status(struct docg4_priv *doc) */ uint16_t flash_status; - unsigned int timeo; + unsigned long timeo; void __iomem *docptr = doc->virtadr; dev_dbg(doc->dev, "%s...\n", __func__); @@ -252,22 +278,18 @@ static int poll_status(struct docg4_priv *doc) /* hardware quirk requires reading twice initially */ flash_status = readw(docptr + DOC_FLASHCONTROL); - timeo = 1000; + timeo = jiffies + msecs_to_jiffies(200); /* generous timeout */ do { cpu_relax(); flash_status = readb(docptr + DOC_FLASHCONTROL); - } while (!(flash_status & DOC_CTRL_FLASHREADY) && --timeo); - + } while (!(flash_status & DOC_CTRL_FLASHREADY) && + time_before(jiffies, timeo)); - if (!timeo) { + if (unlikely(!(flash_status & DOC_CTRL_FLASHREADY))) { dev_err(doc->dev, "%s: timed out!\n", __func__); return NAND_STATUS_FAIL; } - if (unlikely(timeo < 50)) - dev_warn(doc->dev, "%s: nearly timed out; %d remaining\n", - __func__, timeo); - return 0; } @@ -469,7 +491,7 @@ static uint8_t docg4_read_byte(struct mtd_info *mtd) return status; } - dev_warn(doc->dev, "unexpectd call to read_byte()\n"); + dev_warn(doc->dev, "unexpected call to read_byte()\n"); return 0; } @@ -611,6 +633,14 @@ static void write_page_prologue(struct mtd_info *mtd, uint32_t docg4_addr) dev_dbg(doc->dev, "docg4: %s: g4 addr: %x\n", __func__, docg4_addr); sequence_reset(mtd); + + if (unlikely(reliable_mode)) { + writew(DOCG4_SEQ_SETMODE, docptr + DOC_FLASHSEQUENCE); + writew(DOCG4_CMD_FAST_MODE, docptr + DOC_FLASHCOMMAND); + writew(DOC_CMD_RELIABLE_MODE, docptr + DOC_FLASHCOMMAND); + write_nop(docptr); + } + writew(DOCG4_SEQ_PAGEWRITE, docptr + DOC_FLASHSEQUENCE); writew(DOCG4_CMD_PAGEWRITE, docptr + DOC_FLASHCOMMAND); write_nop(docptr); @@ -691,6 +721,15 @@ static void docg4_command(struct mtd_info *mtd, unsigned command, int column, break; case NAND_CMD_SEQIN: + if (unlikely(reliable_mode)) { + uint16_t g4_page = g4_addr >> 16; + + /* writes to odd-numbered 2k pages are invalid */ + if (g4_page & 0x01) + dev_warn(doc->dev, + "invalid reliable mode address\n"); + } + write_page_prologue(mtd, g4_addr); /* hack for deferred write of oob bytes */ @@ -833,7 +872,7 @@ static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, return 0; } -static void docg4_erase_block(struct mtd_info *mtd, int page) +static int docg4_erase_block(struct mtd_info *mtd, int page) { struct nand_chip *nand = mtd->priv; struct docg4_priv *doc = nand->priv; @@ -877,6 +916,8 @@ static void docg4_erase_block(struct mtd_info *mtd, int page) write_nop(docptr); poll_status(doc); write_nop(docptr); + + return nand->waitfunc(mtd, nand); } static int write_page(struct mtd_info *mtd, struct nand_chip *nand, @@ -979,16 +1020,15 @@ static int __init read_factory_bbt(struct mtd_info *mtd) struct docg4_priv *doc = nand->priv; uint32_t g4_addr = mtd_to_docg4_address(DOCG4_FACTORY_BBT_PAGE, 0); uint8_t *buf; - int i, block, status; + int i, block; + __u32 eccfailed_stats = mtd->ecc_stats.failed; buf = kzalloc(DOCG4_PAGE_SIZE, GFP_KERNEL); if (buf == NULL) return -ENOMEM; read_page_prologue(mtd, g4_addr); - status = docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); - if (status) - goto exit; + docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); /* * If no memory-based bbt was created, exit. This will happen if module @@ -1000,6 +1040,20 @@ static int __init read_factory_bbt(struct mtd_info *mtd) if (nand->bbt == NULL) /* no memory-based bbt */ goto exit; + if (mtd->ecc_stats.failed > eccfailed_stats) { + /* + * Whoops, an ecc failure ocurred reading the factory bbt. + * It is stored redundantly, so we get another chance. + */ + eccfailed_stats = mtd->ecc_stats.failed; + docg4_read_page(mtd, nand, buf, 0, DOCG4_REDUNDANT_BBT_PAGE); + if (mtd->ecc_stats.failed > eccfailed_stats) { + dev_warn(doc->dev, + "The factory bbt could not be read!\n"); + goto exit; + } + } + /* * Parse factory bbt and update memory-based bbt. Factory bbt format is * simple: one bit per block, block numbers increase left to right (msb @@ -1019,7 +1073,7 @@ static int __init read_factory_bbt(struct mtd_info *mtd) } exit: kfree(buf); - return status; + return 0; } static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) @@ -1038,7 +1092,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) struct nand_chip *nand = mtd->priv; struct docg4_priv *doc = nand->priv; struct nand_bbt_descr *bbtd = nand->badblock_pattern; - int block = (int)(ofs >> nand->bbt_erase_shift); int page = (int)(ofs >> nand->page_shift); uint32_t g4_addr = mtd_to_docg4_address(page, 0); @@ -1053,9 +1106,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) if (buf == NULL) return -ENOMEM; - /* update bbt in memory */ - nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2); - /* write bit-wise negation of pattern to oob buffer */ memset(nand->oob_poi, 0xff, mtd->oobsize); for (i = 0; i < bbtd->len; i++) @@ -1065,8 +1115,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) write_page_prologue(mtd, g4_addr); docg4_write_page(mtd, nand, buf, 1); ret = pageprog(mtd); - if (!ret) - mtd->ecc_stats.badblocks++; kfree(buf); @@ -1190,8 +1238,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->block_markbad = docg4_block_markbad; nand->read_buf = docg4_read_buf; nand->write_buf = docg4_write_buf16; - nand->scan_bbt = nand_default_bbt; - nand->erase_cmd = docg4_erase_block; + nand->erase = docg4_erase_block; nand->ecc.read_page = docg4_read_page; nand->ecc.write_page = docg4_write_page; nand->ecc.read_page_raw = docg4_read_page_raw; @@ -1313,7 +1360,6 @@ static int __init probe_docg4(struct platform_device *pdev) struct nand_chip *nand = mtd->priv; struct docg4_priv *doc = nand->priv; nand_release(mtd); /* deletes partitions and mtd devices */ - platform_set_drvdata(pdev, NULL); free_bch(doc->bch); kfree(mtd); } @@ -1325,7 +1371,6 @@ static int __exit cleanup_docg4(struct platform_device *pdev) { struct docg4_priv *doc = platform_get_drvdata(pdev); nand_release(doc->mtd); - platform_set_drvdata(pdev, NULL); free_bch(doc->bch); kfree(doc->mtd); iounmap(doc->virtadr); @@ -1342,18 +1387,7 @@ static struct platform_driver docg4_driver = { .remove = __exit_p(cleanup_docg4), }; -static int __init docg4_init(void) -{ - return platform_driver_probe(&docg4_driver, probe_docg4); -} - -static void __exit docg4_exit(void) -{ - platform_driver_unregister(&docg4_driver); -} - -module_init(docg4_init); -module_exit(docg4_exit); +module_platform_driver_probe(docg4_driver, probe_docg4); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mike Dunn"); |
