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 | |
35 | * @ecclayout: private ecc layout for this BCH configuration | |
36 | * @errloc: error location array | |
37 | * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid | |
38 | */ | |
39 | struct nand_bch_control { | |
40 | struct bch_control *bch; | |
41 | struct nand_ecclayout ecclayout; | |
42 | unsigned int *errloc; | |
43 | unsigned char *eccmask; | |
44 | }; | |
45 | ||
46 | /** | |
47 | * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block | |
48 | * @mtd: MTD block structure | |
49 | * @buf: input buffer with raw data | |
50 | * @code: output buffer with ECC | |
51 | */ | |
52 | int nand_bch_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, | |
53 | unsigned char *code) | |
54 | { | |
862eba51 | 55 | const struct nand_chip *chip = mtd_to_nand(mtd); |
193bd400 ID |
56 | struct nand_bch_control *nbc = chip->ecc.priv; |
57 | unsigned int i; | |
58 | ||
59 | memset(code, 0, chip->ecc.bytes); | |
60 | encode_bch(nbc->bch, buf, chip->ecc.size, code); | |
61 | ||
62 | /* apply mask so that an erased page is a valid codeword */ | |
63 | for (i = 0; i < chip->ecc.bytes; i++) | |
64 | code[i] ^= nbc->eccmask[i]; | |
65 | ||
66 | return 0; | |
67 | } | |
68 | EXPORT_SYMBOL(nand_bch_calculate_ecc); | |
69 | ||
70 | /** | |
71 | * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) | |
72 | * @mtd: MTD block structure | |
73 | * @buf: raw data read from the chip | |
74 | * @read_ecc: ECC from the chip | |
75 | * @calc_ecc: the ECC calculated from raw data | |
76 | * | |
77 | * Detect and correct bit errors for a data byte block | |
78 | */ | |
79 | int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, | |
80 | unsigned char *read_ecc, unsigned char *calc_ecc) | |
81 | { | |
862eba51 | 82 | const struct nand_chip *chip = mtd_to_nand(mtd); |
193bd400 ID |
83 | struct nand_bch_control *nbc = chip->ecc.priv; |
84 | unsigned int *errloc = nbc->errloc; | |
85 | int i, count; | |
86 | ||
87 | count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, | |
88 | NULL, errloc); | |
89 | if (count > 0) { | |
90 | for (i = 0; i < count; i++) { | |
91 | if (errloc[i] < (chip->ecc.size*8)) | |
92 | /* error is located in data, correct it */ | |
93 | buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); | |
94 | /* else error in ecc, no action needed */ | |
95 | ||
0a32a102 BN |
96 | pr_debug("%s: corrected bitflip %u\n", __func__, |
97 | errloc[i]); | |
193bd400 ID |
98 | } |
99 | } else if (count < 0) { | |
100 | printk(KERN_ERR "ecc unrecoverable error\n"); | |
6e941192 | 101 | count = -EBADMSG; |
193bd400 ID |
102 | } |
103 | return count; | |
104 | } | |
105 | EXPORT_SYMBOL(nand_bch_correct_data); | |
106 | ||
107 | /** | |
108 | * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction | |
109 | * @mtd: MTD block structure | |
193bd400 ID |
110 | * |
111 | * Returns: | |
112 | * a pointer to a new NAND BCH control structure, or NULL upon failure | |
113 | * | |
114 | * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes | |
115 | * are used to compute BCH parameters m (Galois field order) and t (error | |
116 | * correction capability). @eccbytes should be equal to the number of bytes | |
117 | * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. | |
118 | * | |
119 | * Example: to configure 4 bit correction per 512 bytes, you should pass | |
120 | * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) | |
121 | * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) | |
122 | */ | |
a8c65d50 | 123 | struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) |
193bd400 | 124 | { |
a8c65d50 | 125 | struct nand_chip *nand = mtd_to_nand(mtd); |
193bd400 | 126 | unsigned int m, t, eccsteps, i; |
a8c65d50 | 127 | struct nand_ecclayout *layout = nand->ecc.layout; |
193bd400 ID |
128 | struct nand_bch_control *nbc = NULL; |
129 | unsigned char *erased_page; | |
a8c65d50 BB |
130 | unsigned int eccsize = nand->ecc.size; |
131 | unsigned int eccbytes = nand->ecc.bytes; | |
132 | unsigned int eccstrength = nand->ecc.strength; | |
133 | ||
134 | if (!eccbytes && eccstrength) { | |
135 | eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); | |
136 | nand->ecc.bytes = eccbytes; | |
137 | } | |
193bd400 ID |
138 | |
139 | if (!eccsize || !eccbytes) { | |
140 | printk(KERN_WARNING "ecc parameters not supplied\n"); | |
141 | goto fail; | |
142 | } | |
143 | ||
144 | m = fls(1+8*eccsize); | |
145 | t = (eccbytes*8)/m; | |
146 | ||
147 | nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); | |
148 | if (!nbc) | |
149 | goto fail; | |
150 | ||
151 | nbc->bch = init_bch(m, t, 0); | |
152 | if (!nbc->bch) | |
153 | goto fail; | |
154 | ||
155 | /* verify that eccbytes has the expected value */ | |
156 | if (nbc->bch->ecc_bytes != eccbytes) { | |
157 | printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", | |
158 | eccbytes, nbc->bch->ecc_bytes); | |
159 | goto fail; | |
160 | } | |
161 | ||
162 | eccsteps = mtd->writesize/eccsize; | |
163 | ||
164 | /* if no ecc placement scheme was provided, build one */ | |
a8c65d50 | 165 | if (!layout) { |
193bd400 ID |
166 | |
167 | /* handle large page devices only */ | |
168 | if (mtd->oobsize < 64) { | |
169 | printk(KERN_WARNING "must provide an oob scheme for " | |
170 | "oobsize %d\n", mtd->oobsize); | |
171 | goto fail; | |
172 | } | |
173 | ||
174 | layout = &nbc->ecclayout; | |
175 | layout->eccbytes = eccsteps*eccbytes; | |
176 | ||
177 | /* reserve 2 bytes for bad block marker */ | |
178 | if (layout->eccbytes+2 > mtd->oobsize) { | |
179 | printk(KERN_WARNING "no suitable oob scheme available " | |
180 | "for oobsize %d eccbytes %u\n", mtd->oobsize, | |
181 | eccbytes); | |
182 | goto fail; | |
183 | } | |
184 | /* put ecc bytes at oob tail */ | |
185 | for (i = 0; i < layout->eccbytes; i++) | |
186 | layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; | |
187 | ||
188 | layout->oobfree[0].offset = 2; | |
189 | layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; | |
190 | ||
a8c65d50 | 191 | nand->ecc.layout = layout; |
193bd400 ID |
192 | } |
193 | ||
194 | /* sanity checks */ | |
195 | if (8*(eccsize+eccbytes) >= (1 << m)) { | |
196 | printk(KERN_WARNING "eccsize %u is too large\n", eccsize); | |
197 | goto fail; | |
198 | } | |
a8c65d50 | 199 | if (layout->eccbytes != (eccsteps*eccbytes)) { |
193bd400 ID |
200 | printk(KERN_WARNING "invalid ecc layout\n"); |
201 | goto fail; | |
202 | } | |
203 | ||
204 | nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); | |
205 | nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); | |
206 | if (!nbc->eccmask || !nbc->errloc) | |
207 | goto fail; | |
208 | /* | |
209 | * compute and store the inverted ecc of an erased ecc block | |
210 | */ | |
211 | erased_page = kmalloc(eccsize, GFP_KERNEL); | |
212 | if (!erased_page) | |
213 | goto fail; | |
214 | ||
215 | memset(erased_page, 0xff, eccsize); | |
216 | memset(nbc->eccmask, 0, eccbytes); | |
217 | encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); | |
218 | kfree(erased_page); | |
219 | ||
220 | for (i = 0; i < eccbytes; i++) | |
221 | nbc->eccmask[i] ^= 0xff; | |
222 | ||
a8c65d50 BB |
223 | if (!eccstrength) |
224 | nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); | |
225 | ||
193bd400 ID |
226 | return nbc; |
227 | fail: | |
228 | nand_bch_free(nbc); | |
229 | return NULL; | |
230 | } | |
231 | EXPORT_SYMBOL(nand_bch_init); | |
232 | ||
233 | /** | |
234 | * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources | |
235 | * @nbc: NAND BCH control structure | |
236 | */ | |
237 | void nand_bch_free(struct nand_bch_control *nbc) | |
238 | { | |
239 | if (nbc) { | |
240 | free_bch(nbc->bch); | |
241 | kfree(nbc->errloc); | |
242 | kfree(nbc->eccmask); | |
243 | kfree(nbc); | |
244 | } | |
245 | } | |
246 | EXPORT_SYMBOL(nand_bch_free); | |
247 | ||
248 | MODULE_LICENSE("GPL"); | |
249 | MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); | |
250 | MODULE_DESCRIPTION("NAND software BCH ECC support"); |