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