Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | /* * This file provides ECC correction for more than 1 bit per block of data, * using binary BCH codes. It relies on the generic BCH library lib/bch.c. * * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 or (at your option) any * later version. * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this file; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include <linux/types.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/bitops.h> #include <linux/mtd/mtd.h> #include <linux/mtd/rawnand.h> #include <linux/mtd/nand_bch.h> #include <linux/bch.h> /** * struct nand_bch_control - private NAND BCH control structure * @bch: BCH control structure * @errloc: error location array * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid */ struct nand_bch_control { struct bch_control *bch; unsigned int *errloc; unsigned char *eccmask; }; /** * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block * @mtd: MTD block structure * @buf: input buffer with raw data * @code: output buffer with ECC */ int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, unsigned char *code) { const struct nand_chip *chip = mtd_to_nand(mtd); struct nand_bch_control *nbc = chip->ecc.priv; unsigned int i; memset(code, 0, chip->ecc.bytes); encode_bch(nbc->bch, buf, chip->ecc.size, code); /* apply mask so that an erased page is a valid codeword */ for (i = 0; i < chip->ecc.bytes; i++) code[i] ^= nbc->eccmask[i]; return 0; } EXPORT_SYMBOL(nand_bch_calculate_ecc); /** * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) * @mtd: MTD block structure * @buf: raw data read from the chip * @read_ecc: ECC from the chip * @calc_ecc: the ECC calculated from raw data * * Detect and correct bit errors for a data byte block */ int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) { const struct nand_chip *chip = mtd_to_nand(mtd); struct nand_bch_control *nbc = chip->ecc.priv; unsigned int *errloc = nbc->errloc; int i, count; count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, NULL, errloc); if (count > 0) { for (i = 0; i < count; i++) { if (errloc[i] < (chip->ecc.size*8)) /* error is located in data, correct it */ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); /* else error in ecc, no action needed */ pr_debug("%s: corrected bitflip %u\n", __func__, errloc[i]); } } else if (count < 0) { pr_err("ecc unrecoverable error\n"); count = -EBADMSG; } return count; } EXPORT_SYMBOL(nand_bch_correct_data); /** * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction * @mtd: MTD block structure * * Returns: * a pointer to a new NAND BCH control structure, or NULL upon failure * * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes * are used to compute BCH parameters m (Galois field order) and t (error * correction capability). @eccbytes should be equal to the number of bytes * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. * * Example: to configure 4 bit correction per 512 bytes, you should pass * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) */ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) { struct nand_chip *nand = mtd_to_nand(mtd); unsigned int m, t, eccsteps, i; struct nand_bch_control *nbc = NULL; unsigned char *erased_page; unsigned int eccsize = nand->ecc.size; unsigned int eccbytes = nand->ecc.bytes; unsigned int eccstrength = nand->ecc.strength; if (!eccbytes && eccstrength) { eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); nand->ecc.bytes = eccbytes; } if (!eccsize || !eccbytes) { pr_warn("ecc parameters not supplied\n"); goto fail; } m = fls(1+8*eccsize); t = (eccbytes*8)/m; nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); if (!nbc) goto fail; nbc->bch = init_bch(m, t, 0); if (!nbc->bch) goto fail; /* verify that eccbytes has the expected value */ if (nbc->bch->ecc_bytes != eccbytes) { pr_warn("invalid eccbytes %u, should be %u\n", eccbytes, nbc->bch->ecc_bytes); goto fail; } eccsteps = mtd->writesize/eccsize; /* Check that we have an oob layout description. */ if (!mtd->ooblayout) { pr_warn("missing oob scheme"); goto fail; } /* sanity checks */ if (8*(eccsize+eccbytes) >= (1 << m)) { pr_warn("eccsize %u is too large\n", eccsize); goto fail; } /* * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), * which is called by mtd_ooblayout_count_eccbytes(). * Make sure they are properly initialized before calling * mtd_ooblayout_count_eccbytes(). * FIXME: we should probably rework the sequencing in nand_scan_tail() * to avoid setting those fields twice. */ nand->ecc.steps = eccsteps; nand->ecc.total = eccsteps * eccbytes; if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { pr_warn("invalid ecc layout\n"); goto fail; } nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); if (!nbc->eccmask || !nbc->errloc) goto fail; /* * compute and store the inverted ecc of an erased ecc block */ erased_page = kmalloc(eccsize, GFP_KERNEL); if (!erased_page) goto fail; memset(erased_page, 0xff, eccsize); memset(nbc->eccmask, 0, eccbytes); encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); kfree(erased_page); for (i = 0; i < eccbytes; i++) nbc->eccmask[i] ^= 0xff; if (!eccstrength) nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); return nbc; fail: nand_bch_free(nbc); return NULL; } EXPORT_SYMBOL(nand_bch_init); /** * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources * @nbc: NAND BCH control structure */ void nand_bch_free(struct nand_bch_control *nbc) { if (nbc) { free_bch(nbc->bch); kfree(nbc->errloc); kfree(nbc->eccmask); kfree(nbc); } } EXPORT_SYMBOL(nand_bch_free); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); MODULE_DESCRIPTION("NAND software BCH ECC support"); |