diff options
author | David A. Marlin <dmarlin@redhat.com> | 2005-01-17 13:35:25 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@mtd.linutronix.de> | 2005-05-23 05:26:45 -0400 |
commit | 30f464b74b51127b9b9a170157b75c7e8e80d2c4 (patch) | |
tree | 95a9e2f352612a4bfb94a276c1eaf21b0aec8f25 | |
parent | 28a48de72b876af794853593cc1412119ada9efc (diff) |
[MTD] NAND workaround for AG-AND disturb issue. AG-AND recovery
Added workaround for Renesas AG-AND chips "disturb" issue
for Bad Block Table.
Added support for the device recovery command sequence
for Renesas AG-AND chips.
Signed-off-by: David A. Marlin <dmarlin@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 74 |
1 files changed, 69 insertions, 5 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 44d5b128911f..2ac452e3ad6b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -28,6 +28,20 @@ | |||
28 | * among multiple independend devices. Suggestions and initial patch | 28 | * among multiple independend devices. Suggestions and initial patch |
29 | * from Ben Dooks <ben-mtd@fluff.org> | 29 | * from Ben Dooks <ben-mtd@fluff.org> |
30 | * | 30 | * |
31 | * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. | ||
32 | * Basically, any block not rewritten may lose data when surrounding blocks | ||
33 | * are rewritten many times. JFFS2 ensures this doesn't happen for blocks | ||
34 | * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they | ||
35 | * do not lose data, force them to be rewritten when some of the surrounding | ||
36 | * blocks are erased. Rather than tracking a specific nearby block (which | ||
37 | * could itself go bad), use a page address 'mask' to select several blocks | ||
38 | * in the same area, and rewrite the BBT when any of them are erased. | ||
39 | * | ||
40 | * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas | ||
41 | * AG-AND chips. If there was a sudden loss of power during an erase operation, | ||
42 | * a "device recovery" operation must be performed when power is restored | ||
43 | * to ensure correct operation. | ||
44 | * | ||
31 | * Credits: | 45 | * Credits: |
32 | * David Woodhouse for adding multichip support | 46 | * David Woodhouse for adding multichip support |
33 | * | 47 | * |
@@ -41,7 +55,7 @@ | |||
41 | * The AG-AND chips have nice features for speed improvement, | 55 | * The AG-AND chips have nice features for speed improvement, |
42 | * which are not supported yet. Read / program 4 pages in one go. | 56 | * which are not supported yet. Read / program 4 pages in one go. |
43 | * | 57 | * |
44 | * $Id: nand_base.c,v 1.126 2004/12/13 11:22:25 lavinen Exp $ | 58 | * $Id: nand_base.c,v 1.127 2005/01/17 18:35:22 dmarlin Exp $ |
45 | * | 59 | * |
46 | * This program is free software; you can redistribute it and/or modify | 60 | * This program is free software; you can redistribute it and/or modify |
47 | * it under the terms of the GNU General Public License version 2 as | 61 | * it under the terms of the GNU General Public License version 2 as |
@@ -619,7 +633,7 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, | |||
619 | /* Begin command latch cycle */ | 633 | /* Begin command latch cycle */ |
620 | this->hwcontrol(mtd, NAND_CTL_SETCLE); | 634 | this->hwcontrol(mtd, NAND_CTL_SETCLE); |
621 | /* Write out the command to the device. */ | 635 | /* Write out the command to the device. */ |
622 | this->write_byte(mtd, command); | 636 | this->write_byte(mtd, (command & 0xff)); |
623 | /* End command latch cycle */ | 637 | /* End command latch cycle */ |
624 | this->hwcontrol(mtd, NAND_CTL_CLRCLE); | 638 | this->hwcontrol(mtd, NAND_CTL_CLRCLE); |
625 | 639 | ||
@@ -647,8 +661,8 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, | |||
647 | 661 | ||
648 | /* | 662 | /* |
649 | * program and erase have their own busy handlers | 663 | * program and erase have their own busy handlers |
650 | * status and sequential in needs no delay | 664 | * status, sequential in, and deplete1 need no delay |
651 | */ | 665 | */ |
652 | switch (command) { | 666 | switch (command) { |
653 | 667 | ||
654 | case NAND_CMD_CACHEDPROG: | 668 | case NAND_CMD_CACHEDPROG: |
@@ -657,8 +671,19 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, | |||
657 | case NAND_CMD_ERASE2: | 671 | case NAND_CMD_ERASE2: |
658 | case NAND_CMD_SEQIN: | 672 | case NAND_CMD_SEQIN: |
659 | case NAND_CMD_STATUS: | 673 | case NAND_CMD_STATUS: |
674 | case NAND_CMD_DEPLETE1: | ||
660 | return; | 675 | return; |
661 | 676 | ||
677 | /* | ||
678 | * read error status commands require only a short delay | ||
679 | */ | ||
680 | case NAND_CMD_STATUS_ERROR: | ||
681 | case NAND_CMD_STATUS_ERROR0: | ||
682 | case NAND_CMD_STATUS_ERROR1: | ||
683 | case NAND_CMD_STATUS_ERROR2: | ||
684 | case NAND_CMD_STATUS_ERROR3: | ||
685 | udelay(this->chip_delay); | ||
686 | return; | ||
662 | 687 | ||
663 | case NAND_CMD_RESET: | 688 | case NAND_CMD_RESET: |
664 | if (this->dev_ready) | 689 | if (this->dev_ready) |
@@ -1051,7 +1076,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, | |||
1051 | } | 1076 | } |
1052 | 1077 | ||
1053 | /* Grab the lock and see if the device is available */ | 1078 | /* Grab the lock and see if the device is available */ |
1054 | nand_get_device (this, mtd ,FL_READING); | 1079 | nand_get_device (this, mtd, FL_READING); |
1055 | 1080 | ||
1056 | /* use userspace supplied oobinfo, if zero */ | 1081 | /* use userspace supplied oobinfo, if zero */ |
1057 | if (oobsel == NULL) | 1082 | if (oobsel == NULL) |
@@ -1987,6 +2012,7 @@ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) | |||
1987 | return nand_erase_nand (mtd, instr, 0); | 2012 | return nand_erase_nand (mtd, instr, 0); |
1988 | } | 2013 | } |
1989 | 2014 | ||
2015 | #define BBT_PAGE_MASK 0xffffff3f | ||
1990 | /** | 2016 | /** |
1991 | * nand_erase_intern - [NAND Interface] erase block(s) | 2017 | * nand_erase_intern - [NAND Interface] erase block(s) |
1992 | * @mtd: MTD device structure | 2018 | * @mtd: MTD device structure |
@@ -1999,6 +2025,10 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb | |||
1999 | { | 2025 | { |
2000 | int page, len, status, pages_per_block, ret, chipnr; | 2026 | int page, len, status, pages_per_block, ret, chipnr; |
2001 | struct nand_chip *this = mtd->priv; | 2027 | struct nand_chip *this = mtd->priv; |
2028 | int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */ | ||
2029 | unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */ | ||
2030 | /* It is used to see if the current page is in the same */ | ||
2031 | /* 256 block group and the same bank as the bbt. */ | ||
2002 | 2032 | ||
2003 | DEBUG (MTD_DEBUG_LEVEL3, | 2033 | DEBUG (MTD_DEBUG_LEVEL3, |
2004 | "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); | 2034 | "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); |
@@ -2044,6 +2074,13 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb | |||
2044 | goto erase_exit; | 2074 | goto erase_exit; |
2045 | } | 2075 | } |
2046 | 2076 | ||
2077 | /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */ | ||
2078 | if (this->options & BBT_AUTO_REFRESH) { | ||
2079 | bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; | ||
2080 | } else { | ||
2081 | bbt_masked_page = 0xffffffff; /* should not match anything */ | ||
2082 | } | ||
2083 | |||
2047 | /* Loop through the pages */ | 2084 | /* Loop through the pages */ |
2048 | len = instr->len; | 2085 | len = instr->len; |
2049 | 2086 | ||
@@ -2073,6 +2110,14 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb | |||
2073 | instr->fail_addr = (page << this->page_shift); | 2110 | instr->fail_addr = (page << this->page_shift); |
2074 | goto erase_exit; | 2111 | goto erase_exit; |
2075 | } | 2112 | } |
2113 | |||
2114 | /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */ | ||
2115 | if (this->options & BBT_AUTO_REFRESH) { | ||
2116 | if (((page & BBT_PAGE_MASK) == bbt_masked_page) && | ||
2117 | (page != this->bbt_td->pages[chipnr])) { | ||
2118 | rewrite_bbt[chipnr] = (page << this->page_shift); | ||
2119 | } | ||
2120 | } | ||
2076 | 2121 | ||
2077 | /* Increment page address and decrement length */ | 2122 | /* Increment page address and decrement length */ |
2078 | len -= (1 << this->phys_erase_shift); | 2123 | len -= (1 << this->phys_erase_shift); |
@@ -2083,6 +2128,13 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb | |||
2083 | chipnr++; | 2128 | chipnr++; |
2084 | this->select_chip(mtd, -1); | 2129 | this->select_chip(mtd, -1); |
2085 | this->select_chip(mtd, chipnr); | 2130 | this->select_chip(mtd, chipnr); |
2131 | |||
2132 | /* if BBT requires refresh and BBT-PERCHIP, | ||
2133 | * set the BBT page mask to see if this BBT should be rewritten */ | ||
2134 | if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) { | ||
2135 | bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; | ||
2136 | } | ||
2137 | |||
2086 | } | 2138 | } |
2087 | } | 2139 | } |
2088 | instr->state = MTD_ERASE_DONE; | 2140 | instr->state = MTD_ERASE_DONE; |
@@ -2097,6 +2149,18 @@ erase_exit: | |||
2097 | /* Deselect and wake up anyone waiting on the device */ | 2149 | /* Deselect and wake up anyone waiting on the device */ |
2098 | nand_release_device(mtd); | 2150 | nand_release_device(mtd); |
2099 | 2151 | ||
2152 | /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */ | ||
2153 | if ((this->options & BBT_AUTO_REFRESH) && (!ret)) { | ||
2154 | for (chipnr = 0; chipnr < this->numchips; chipnr++) { | ||
2155 | if (rewrite_bbt[chipnr]) { | ||
2156 | /* update the BBT for chip */ | ||
2157 | DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", | ||
2158 | chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]); | ||
2159 | nand_update_bbt (mtd, rewrite_bbt[chipnr]); | ||
2160 | } | ||
2161 | } | ||
2162 | } | ||
2163 | |||
2100 | /* Return more or less happy */ | 2164 | /* Return more or less happy */ |
2101 | return ret; | 2165 | return ret; |
2102 | } | 2166 | } |