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, |
Brian Norris | 1fbb938 | 2012-05-02 10:14:55 -0700 | [diff] [blame] | 25 | struct nand_chip *chip, uint8_t *buf, int oob_required, int page); |
Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 26 | static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, |
Brian Norris | 1fbb938 | 2012-05-02 10:14:55 -0700 | [diff] [blame] | 27 | struct nand_chip *chip, const uint8_t *buf, int oob_required); |
Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 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 |
Brian Norris | 1fbb938 | 2012-05-02 10:14:55 -0700 | [diff] [blame] | 106 | * @oob_required: caller expects OOB data read to chip->oob_poi |
Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 107 | * |
| 108 | ***************************************************************************/ |
| 109 | static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, |
| 110 | struct nand_chip *chip, uint8_t * buf, |
Brian Norris | 1fbb938 | 2012-05-02 10:14:55 -0700 | [diff] [blame] | 111 | int oob_required, int page) |
Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 112 | { |
| 113 | int sectorIdx = 0; |
| 114 | int eccsize = chip->ecc.size; |
| 115 | int eccsteps = chip->ecc.steps; |
| 116 | uint8_t *datap = buf; |
| 117 | uint8_t eccCalc[NAND_ECC_NUM_BYTES]; |
| 118 | int sectorOobSize = mtd->oobsize / eccsteps; |
| 119 | int stat; |
Mike Dunn | 3f91e94 | 2012-04-25 12:06:09 -0700 | [diff] [blame] | 120 | unsigned int max_bitflips = 0; |
Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 121 | |
| 122 | for (sectorIdx = 0; sectorIdx < eccsteps; |
| 123 | sectorIdx++, datap += eccsize) { |
| 124 | if (sectorIdx > 0) { |
| 125 | /* Seek to page location within sector */ |
| 126 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize, |
| 127 | -1); |
| 128 | } |
| 129 | |
| 130 | /* Enable hardware ECC before reading the buf */ |
| 131 | nand_bcm_umi_bch_enable_read_hwecc(); |
| 132 | |
| 133 | /* Read in data */ |
| 134 | bcm_umi_nand_read_buf(mtd, datap, eccsize); |
| 135 | |
| 136 | /* Pause hardware ECC after reading the buf */ |
| 137 | nand_bcm_umi_bch_pause_read_ecc_calc(); |
| 138 | |
| 139 | /* Read the OOB ECC */ |
| 140 | chip->cmdfunc(mtd, NAND_CMD_RNDOUT, |
| 141 | mtd->writesize + sectorIdx * sectorOobSize, -1); |
| 142 | nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc, |
| 143 | NAND_ECC_NUM_BYTES, |
| 144 | chip->oob_poi + |
| 145 | sectorIdx * sectorOobSize); |
| 146 | |
| 147 | /* Correct any ECC detected errors */ |
| 148 | stat = |
| 149 | nand_bcm_umi_bch_correct_page(datap, eccCalc, |
| 150 | NAND_ECC_NUM_BYTES); |
| 151 | |
| 152 | /* Update Stats */ |
| 153 | if (stat < 0) { |
| 154 | #if defined(NAND_BCM_UMI_DEBUG) |
| 155 | printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n", |
| 156 | __func__, sectorIdx); |
| 157 | printk(KERN_WARNING |
| 158 | "%s data %02x %02x %02x %02x " |
| 159 | "%02x %02x %02x %02x\n", |
| 160 | __func__, datap[0], datap[1], datap[2], datap[3], |
| 161 | datap[4], datap[5], datap[6], datap[7]); |
| 162 | printk(KERN_WARNING |
| 163 | "%s ecc %02x %02x %02x %02x " |
| 164 | "%02x %02x %02x %02x %02x %02x " |
| 165 | "%02x %02x %02x\n", |
| 166 | __func__, eccCalc[0], eccCalc[1], eccCalc[2], |
| 167 | eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6], |
| 168 | eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10], |
| 169 | eccCalc[11], eccCalc[12]); |
| 170 | BUG(); |
| 171 | #endif |
| 172 | mtd->ecc_stats.failed++; |
| 173 | } else { |
| 174 | #if defined(NAND_BCM_UMI_DEBUG) |
| 175 | if (stat > 0) { |
| 176 | printk(KERN_INFO |
| 177 | "%s %d correctable_errors detected\n", |
| 178 | __func__, stat); |
| 179 | } |
| 180 | #endif |
| 181 | mtd->ecc_stats.corrected += stat; |
Mike Dunn | 3f91e94 | 2012-04-25 12:06:09 -0700 | [diff] [blame] | 182 | max_bitflips = max_t(unsigned int, max_bitflips, stat); |
Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 183 | } |
| 184 | } |
Mike Dunn | 3f91e94 | 2012-04-25 12:06:09 -0700 | [diff] [blame] | 185 | return max_bitflips; |
Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | /**************************************************************************** |
| 189 | * |
| 190 | * bcm_umi_bch_write_page_hwecc - hardware ecc based page write function |
| 191 | * @mtd: mtd info structure |
| 192 | * @chip: nand chip info structure |
| 193 | * @buf: data buffer |
Brian Norris | 1fbb938 | 2012-05-02 10:14:55 -0700 | [diff] [blame] | 194 | * @oob_required: must write chip->oob_poi to OOB |
Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 195 | * |
| 196 | ***************************************************************************/ |
| 197 | static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, |
Brian Norris | 1fbb938 | 2012-05-02 10:14:55 -0700 | [diff] [blame] | 198 | struct nand_chip *chip, const uint8_t *buf, int oob_required) |
Leo (Hao) Chen | 266dead | 2009-10-09 19:13:08 -0700 | [diff] [blame] | 199 | { |
| 200 | int sectorIdx = 0; |
| 201 | int eccsize = chip->ecc.size; |
| 202 | int eccsteps = chip->ecc.steps; |
| 203 | const uint8_t *datap = buf; |
| 204 | uint8_t *oobp = chip->oob_poi; |
| 205 | int sectorOobSize = mtd->oobsize / eccsteps; |
| 206 | |
| 207 | for (sectorIdx = 0; sectorIdx < eccsteps; |
| 208 | sectorIdx++, datap += eccsize, oobp += sectorOobSize) { |
| 209 | /* Enable hardware ECC before writing the buf */ |
| 210 | nand_bcm_umi_bch_enable_write_hwecc(); |
| 211 | bcm_umi_nand_write_buf(mtd, datap, eccsize); |
| 212 | nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp, |
| 213 | NAND_ECC_NUM_BYTES); |
| 214 | } |
| 215 | |
| 216 | bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); |
| 217 | } |