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 | { | |
55 | const struct nand_chip *chip = mtd->priv; | |
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 | { | |
82 | const struct nand_chip *chip = mtd->priv; | |
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"); | |
101 | count = -1; | |
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 | |
110 | * @eccsize: ecc block size in bytes | |
111 | * @eccbytes: ecc length in bytes | |
112 | * @ecclayout: output default layout | |
113 | * | |
114 | * Returns: | |
115 | * a pointer to a new NAND BCH control structure, or NULL upon failure | |
116 | * | |
117 | * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes | |
118 | * are used to compute BCH parameters m (Galois field order) and t (error | |
119 | * correction capability). @eccbytes should be equal to the number of bytes | |
120 | * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. | |
121 | * | |
122 | * Example: to configure 4 bit correction per 512 bytes, you should pass | |
123 | * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) | |
124 | * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) | |
125 | */ | |
126 | struct nand_bch_control * | |
127 | nand_bch_init(struct mtd_info *mtd, unsigned int eccsize, unsigned int eccbytes, | |
128 | struct nand_ecclayout **ecclayout) | |
129 | { | |
130 | unsigned int m, t, eccsteps, i; | |
131 | struct nand_ecclayout *layout; | |
132 | struct nand_bch_control *nbc = NULL; | |
133 | unsigned char *erased_page; | |
134 | ||
135 | if (!eccsize || !eccbytes) { | |
136 | printk(KERN_WARNING "ecc parameters not supplied\n"); | |
137 | goto fail; | |
138 | } | |
139 | ||
140 | m = fls(1+8*eccsize); | |
141 | t = (eccbytes*8)/m; | |
142 | ||
143 | nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); | |
144 | if (!nbc) | |
145 | goto fail; | |
146 | ||
147 | nbc->bch = init_bch(m, t, 0); | |
148 | if (!nbc->bch) | |
149 | goto fail; | |
150 | ||
151 | /* verify that eccbytes has the expected value */ | |
152 | if (nbc->bch->ecc_bytes != eccbytes) { | |
153 | printk(KERN_WARNING "invalid eccbytes %u, should be %u\n", | |
154 | eccbytes, nbc->bch->ecc_bytes); | |
155 | goto fail; | |
156 | } | |
157 | ||
158 | eccsteps = mtd->writesize/eccsize; | |
159 | ||
160 | /* if no ecc placement scheme was provided, build one */ | |
161 | if (!*ecclayout) { | |
162 | ||
163 | /* handle large page devices only */ | |
164 | if (mtd->oobsize < 64) { | |
165 | printk(KERN_WARNING "must provide an oob scheme for " | |
166 | "oobsize %d\n", mtd->oobsize); | |
167 | goto fail; | |
168 | } | |
169 | ||
170 | layout = &nbc->ecclayout; | |
171 | layout->eccbytes = eccsteps*eccbytes; | |
172 | ||
173 | /* reserve 2 bytes for bad block marker */ | |
174 | if (layout->eccbytes+2 > mtd->oobsize) { | |
175 | printk(KERN_WARNING "no suitable oob scheme available " | |
176 | "for oobsize %d eccbytes %u\n", mtd->oobsize, | |
177 | eccbytes); | |
178 | goto fail; | |
179 | } | |
180 | /* put ecc bytes at oob tail */ | |
181 | for (i = 0; i < layout->eccbytes; i++) | |
182 | layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; | |
183 | ||
184 | layout->oobfree[0].offset = 2; | |
185 | layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; | |
186 | ||
187 | *ecclayout = layout; | |
188 | } | |
189 | ||
190 | /* sanity checks */ | |
191 | if (8*(eccsize+eccbytes) >= (1 << m)) { | |
192 | printk(KERN_WARNING "eccsize %u is too large\n", eccsize); | |
193 | goto fail; | |
194 | } | |
195 | if ((*ecclayout)->eccbytes != (eccsteps*eccbytes)) { | |
196 | printk(KERN_WARNING "invalid ecc layout\n"); | |
197 | goto fail; | |
198 | } | |
199 | ||
200 | nbc->eccmask = kmalloc(eccbytes, GFP_KERNEL); | |
201 | nbc->errloc = kmalloc(t*sizeof(*nbc->errloc), GFP_KERNEL); | |
202 | if (!nbc->eccmask || !nbc->errloc) | |
203 | goto fail; | |
204 | /* | |
205 | * compute and store the inverted ecc of an erased ecc block | |
206 | */ | |
207 | erased_page = kmalloc(eccsize, GFP_KERNEL); | |
208 | if (!erased_page) | |
209 | goto fail; | |
210 | ||
211 | memset(erased_page, 0xff, eccsize); | |
212 | memset(nbc->eccmask, 0, eccbytes); | |
213 | encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); | |
214 | kfree(erased_page); | |
215 | ||
216 | for (i = 0; i < eccbytes; i++) | |
217 | nbc->eccmask[i] ^= 0xff; | |
218 | ||
219 | return nbc; | |
220 | fail: | |
221 | nand_bch_free(nbc); | |
222 | return NULL; | |
223 | } | |
224 | EXPORT_SYMBOL(nand_bch_init); | |
225 | ||
226 | /** | |
227 | * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources | |
228 | * @nbc: NAND BCH control structure | |
229 | */ | |
230 | void nand_bch_free(struct nand_bch_control *nbc) | |
231 | { | |
232 | if (nbc) { | |
233 | free_bch(nbc->bch); | |
234 | kfree(nbc->errloc); | |
235 | kfree(nbc->eccmask); | |
236 | kfree(nbc); | |
237 | } | |
238 | } | |
239 | EXPORT_SYMBOL(nand_bch_free); | |
240 | ||
241 | MODULE_LICENSE("GPL"); | |
242 | MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); | |
243 | MODULE_DESCRIPTION("NAND software BCH ECC support"); |