Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 1 | /***************************************************************************** |
| 2 | * Copyright 2004 - 2009 Broadcom Corporation. All rights reserved. |
| 3 | * |
| 4 | * Unless you and Broadcom execute a separate written software license |
| 5 | * agreement governing use of this software, this software is licensed to you |
| 6 | * under the terms of the GNU General Public License version 2, available at |
| 7 | * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). |
| 8 | * |
| 9 | * Notwithstanding the above, under no circumstances may you combine this |
| 10 | * software in any way with any other Broadcom software provided under a |
| 11 | * license other than the GPL, without Broadcom's express prior written |
| 12 | * consent. |
| 13 | *****************************************************************************/ |
| 14 | |
| 15 | /* ---- Include Files ---------------------------------------------------- */ |
| 16 | #include "nand_bcm_umi.h" |
| 17 | |
| 18 | /* ---- External Variable Declarations ----------------------------------- */ |
| 19 | /* ---- External Function Prototypes ------------------------------------- */ |
| 20 | /* ---- Public Variables ------------------------------------------------- */ |
| 21 | /* ---- Private Constants and Types -------------------------------------- */ |
| 22 | |
| 23 | /* ---- Private Function Prototypes -------------------------------------- */ |
| 24 | static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, |
| 25 | struct nand_chip *chip, uint8_t *buf, int page); |
| 26 | static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, |
| 27 | struct nand_chip *chip, const uint8_t *buf); |
| 28 | |
| 29 | /* ---- Private Variables ------------------------------------------------ */ |
| 30 | |
| 31 | /* |
| 32 | ** nand_hw_eccoob |
| 33 | ** New oob placement block for use with hardware ecc generation. |
| 34 | */ |
| 35 | static struct nand_ecclayout nand_hw_eccoob_512 = { |
| 36 | /* Reserve 5 for BI indicator */ |
| 37 | .oobfree = { |
| 38 | #if (NAND_ECC_NUM_BYTES > 3) |
| 39 | {.offset = 0, .length = 2} |
| 40 | #else |
| 41 | {.offset = 0, .length = 5}, |
| 42 | {.offset = 6, .length = 7} |
| 43 | #endif |
| 44 | } |
| 45 | }; |
| 46 | |
| 47 | /* |
| 48 | ** We treat the OOB for a 2K page as if it were 4 512 byte oobs, |
| 49 | ** except the BI is at byte 0. |
| 50 | */ |
| 51 | static struct nand_ecclayout nand_hw_eccoob_2048 = { |
| 52 | /* Reserve 0 as BI indicator */ |
| 53 | .oobfree = { |
| 54 | #if (NAND_ECC_NUM_BYTES > 10) |
| 55 | {.offset = 1, .length = 2}, |
| 56 | #elif (NAND_ECC_NUM_BYTES > 7) |
| 57 | {.offset = 1, .length = 5}, |
| 58 | {.offset = 16, .length = 6}, |
| 59 | {.offset = 32, .length = 6}, |
| 60 | {.offset = 48, .length = 6} |
| 61 | #else |
| 62 | {.offset = 1, .length = 8}, |
| 63 | {.offset = 16, .length = 9}, |
| 64 | {.offset = 32, .length = 9}, |
| 65 | {.offset = 48, .length = 9} |
| 66 | #endif |
| 67 | } |
| 68 | }; |
| 69 | |
| 70 | /* We treat the OOB for a 4K page as if it were 8 512 byte oobs, |
| 71 | * except the BI is at byte 0. */ |
| 72 | static struct nand_ecclayout nand_hw_eccoob_4096 = { |
| 73 | /* Reserve 0 as BI indicator */ |
| 74 | .oobfree = { |
| 75 | #if (NAND_ECC_NUM_BYTES > 10) |
| 76 | {.offset = 1, .length = 2}, |
| 77 | {.offset = 16, .length = 3}, |
| 78 | {.offset = 32, .length = 3}, |
| 79 | {.offset = 48, .length = 3}, |
| 80 | {.offset = 64, .length = 3}, |
| 81 | {.offset = 80, .length = 3}, |
| 82 | {.offset = 96, .length = 3}, |
| 83 | {.offset = 112, .length = 3} |
| 84 | #else |
| 85 | {.offset = 1, .length = 5}, |
| 86 | {.offset = 16, .length = 6}, |
| 87 | {.offset = 32, .length = 6}, |
| 88 | {.offset = 48, .length = 6}, |
| 89 | {.offset = 64, .length = 6}, |
| 90 | {.offset = 80, .length = 6}, |
| 91 | {.offset = 96, .length = 6}, |
| 92 | {.offset = 112, .length = 6} |
| 93 | #endif |
| 94 | } |
| 95 | }; |
| 96 | |
| 97 | /* ---- Private Functions ------------------------------------------------ */ |
| 98 | /* ==== Public Functions ================================================= */ |
| 99 | |
| 100 | /**************************************************************************** |
| 101 | * |
| 102 | * bcm_umi_bch_read_page_hwecc - hardware ecc based page read function |
| 103 | * @mtd: mtd info structure |
| 104 | * @chip: nand chip info structure |
| 105 | * @buf: buffer to store read data |
| 106 | * |
| 107 | ***************************************************************************/ |
| 108 | static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, |
| 109 | struct nand_chip *chip, uint8_t * buf, |
| 110 | int page) |
| 111 | { |
| 112 | int sectorIdx = 0; |
| 113 | int eccsize = chip->ecc.size; |
| 114 | int eccsteps = chip->ecc.steps; |
| 115 | uint8_t *datap = buf; |
| 116 | uint8_t eccCalc[NAND_ECC_NUM_BYTES]; |
| 117 | int sectorOobSize = mtd->oobsize / eccsteps; |
| 118 | int stat; |
| 119 | |
| 120 | for (sectorIdx = 0; sectorIdx < eccsteps; |
| 121 | sectorIdx++, datap += eccsize) { |
| 122 | if (sectorIdx > 0) { |
| 123 | /* Seek to page location within sector */ |
| 124 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize, |
| 125 | -1); |
| 126 | } |
| 127 | |
| 128 | /* Enable hardware ECC before reading the buf */ |
| 129 | nand_bcm_umi_bch_enable_read_hwecc(); |
| 130 | |
| 131 | /* Read in data */ |
| 132 | bcm_umi_nand_read_buf(mtd, datap, eccsize); |
| 133 | |
| 134 | /* Pause hardware ECC after reading the buf */ |
| 135 | nand_bcm_umi_bch_pause_read_ecc_calc(); |
| 136 | |
| 137 | /* Read the OOB ECC */ |
| 138 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, |
| 139 | mtd->writesize + sectorIdx * sectorOobSize, -1); |
| 140 | nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc, |
| 141 | NAND_ECC_NUM_BYTES, |
| 142 | chip->oob_poi + |
| 143 | sectorIdx * sectorOobSize); |
| 144 | |
| 145 | /* Correct any ECC detected errors */ |
| 146 | stat = |
| 147 | nand_bcm_umi_bch_correct_page(datap, eccCalc, |
| 148 | NAND_ECC_NUM_BYTES); |
| 149 | |
| 150 | /* Update Stats */ |
| 151 | if (stat < 0) { |
| 152 | #if defined(NAND_BCM_UMI_DEBUG) |
| 153 | printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n", |
| 154 | __func__, sectorIdx); |
| 155 | printk(KERN_WARNING |
| 156 | "%s data %02x %02x %02x %02x " |
| 157 | "%02x %02x %02x %02x\n", |
| 158 | __func__, datap[0], datap[1], datap[2], datap[3], |
| 159 | datap[4], datap[5], datap[6], datap[7]); |
| 160 | printk(KERN_WARNING |
| 161 | "%s ecc %02x %02x %02x %02x " |
| 162 | "%02x %02x %02x %02x %02x %02x " |
| 163 | "%02x %02x %02x\n", |
| 164 | __func__, eccCalc[0], eccCalc[1], eccCalc[2], |
| 165 | eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6], |
| 166 | eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10], |
| 167 | eccCalc[11], eccCalc[12]); |
| 168 | BUG(); |
| 169 | #endif |
| 170 | mtd->ecc_stats.failed++; |
| 171 | } else { |
| 172 | #if defined(NAND_BCM_UMI_DEBUG) |
| 173 | if (stat > 0) { |
| 174 | printk(KERN_INFO |
| 175 | "%s %d correctable_errors detected\n", |
| 176 | __func__, stat); |
| 177 | } |
| 178 | #endif |
| 179 | mtd->ecc_stats.corrected += stat; |
| 180 | } |
| 181 | } |
| 182 | return 0; |
| 183 | } |
| 184 | |
| 185 | /**************************************************************************** |
| 186 | * |
| 187 | * bcm_umi_bch_write_page_hwecc - hardware ecc based page write function |
| 188 | * @mtd: mtd info structure |
| 189 | * @chip: nand chip info structure |
| 190 | * @buf: data buffer |
| 191 | * |
| 192 | ***************************************************************************/ |
| 193 | static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, |
| 194 | struct nand_chip *chip, const uint8_t *buf) |
| 195 | { |
| 196 | int sectorIdx = 0; |
| 197 | int eccsize = chip->ecc.size; |
| 198 | int eccsteps = chip->ecc.steps; |
| 199 | const uint8_t *datap = buf; |
| 200 | uint8_t *oobp = chip->oob_poi; |
| 201 | int sectorOobSize = mtd->oobsize / eccsteps; |
| 202 | |
| 203 | for (sectorIdx = 0; sectorIdx < eccsteps; |
| 204 | sectorIdx++, datap += eccsize, oobp += sectorOobSize) { |
| 205 | /* Enable hardware ECC before writing the buf */ |
| 206 | nand_bcm_umi_bch_enable_write_hwecc(); |
| 207 | bcm_umi_nand_write_buf(mtd, datap, eccsize); |
| 208 | nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp, |
| 209 | NAND_ECC_NUM_BYTES); |
| 210 | } |
| 211 | |
| 212 | bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); |
| 213 | } |