Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * This file contains an ECC algorithm from Toshiba that detects and | |
3 | * corrects 1 bit errors in a 256 byte block of data. | |
4 | * | |
5 | * drivers/mtd/nand/nand_ecc.c | |
6 | * | |
7 | * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) | |
8 | * Toshiba America Electronics Components, Inc. | |
9 | * | |
819d6a32 TG |
10 | * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de> |
11 | * | |
61b03bd7 | 12 | * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $ |
1da177e4 LT |
13 | * |
14 | * This file is free software; you can redistribute it and/or modify it | |
15 | * under the terms of the GNU General Public License as published by the | |
16 | * Free Software Foundation; either version 2 or (at your option) any | |
17 | * later version. | |
61b03bd7 | 18 | * |
1da177e4 LT |
19 | * This file is distributed in the hope that it will be useful, but WITHOUT |
20 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
21 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
22 | * for more details. | |
61b03bd7 | 23 | * |
1da177e4 LT |
24 | * You should have received a copy of the GNU General Public License along |
25 | * with this file; if not, write to the Free Software Foundation, Inc., | |
26 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
61b03bd7 | 27 | * |
1da177e4 LT |
28 | * As a special exception, if other files instantiate templates or use |
29 | * macros or inline functions from these files, or you compile these | |
30 | * files and link them with other works to produce a work based on these | |
31 | * files, these files do not by themselves cause the resulting work to be | |
32 | * covered by the GNU General Public License. However the source code for | |
33 | * these files must still be made available in accordance with section (3) | |
34 | * of the GNU General Public License. | |
61b03bd7 | 35 | * |
1da177e4 LT |
36 | * This exception does not invalidate any other reasons why a work based on |
37 | * this file might be covered by the GNU General Public License. | |
38 | */ | |
39 | ||
40 | #include <linux/types.h> | |
41 | #include <linux/kernel.h> | |
42 | #include <linux/module.h> | |
43 | #include <linux/mtd/nand_ecc.h> | |
44 | ||
45 | /* | |
46 | * Pre-calculated 256-way 1 byte column parity | |
47 | */ | |
48 | static const u_char nand_ecc_precalc_table[] = { | |
49 | 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, | |
50 | 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, | |
51 | 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, | |
52 | 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, | |
53 | 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, | |
54 | 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, | |
55 | 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, | |
56 | 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, | |
57 | 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, | |
58 | 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, | |
59 | 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, | |
60 | 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, | |
61 | 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, | |
62 | 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, | |
63 | 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, | |
64 | 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 | |
65 | }; | |
66 | ||
1da177e4 | 67 | /** |
819d6a32 TG |
68 | * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code |
69 | * for 256 byte block | |
1da177e4 LT |
70 | * @mtd: MTD block structure |
71 | * @dat: raw data | |
72 | * @ecc_code: buffer for ECC | |
73 | */ | |
819d6a32 TG |
74 | int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, |
75 | u_char *ecc_code) | |
1da177e4 | 76 | { |
819d6a32 TG |
77 | uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; |
78 | int i; | |
61b03bd7 | 79 | |
1da177e4 LT |
80 | /* Initialize variables */ |
81 | reg1 = reg2 = reg3 = 0; | |
61b03bd7 TG |
82 | |
83 | /* Build up column parity */ | |
819d6a32 | 84 | for(i = 0; i < 256; i++) { |
1da177e4 | 85 | /* Get CP0 - CP5 from table */ |
819d6a32 | 86 | idx = nand_ecc_precalc_table[*dat++]; |
1da177e4 | 87 | reg1 ^= (idx & 0x3f); |
61b03bd7 | 88 | |
1da177e4 LT |
89 | /* All bit XOR = 1 ? */ |
90 | if (idx & 0x40) { | |
819d6a32 TG |
91 | reg3 ^= (uint8_t) i; |
92 | reg2 ^= ~((uint8_t) i); | |
1da177e4 LT |
93 | } |
94 | } | |
61b03bd7 | 95 | |
1da177e4 | 96 | /* Create non-inverted ECC code from line parity */ |
819d6a32 TG |
97 | tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ |
98 | tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ | |
99 | tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ | |
100 | tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ | |
101 | tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ | |
102 | tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ | |
103 | tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ | |
104 | tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ | |
105 | ||
106 | tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ | |
107 | tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ | |
108 | tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ | |
109 | tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ | |
110 | tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ | |
111 | tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ | |
112 | tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ | |
113 | tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ | |
61b03bd7 | 114 | |
1da177e4 | 115 | /* Calculate final ECC code */ |
819d6a32 TG |
116 | #ifdef CONFIG_NAND_ECC_SMC |
117 | ecc_code[0] = ~tmp2; | |
118 | ecc_code[1] = ~tmp1; | |
119 | #else | |
120 | ecc_code[0] = ~tmp1; | |
121 | ecc_code[1] = ~tmp2; | |
122 | #endif | |
1da177e4 | 123 | ecc_code[2] = ((~reg1) << 2) | 0x03; |
819d6a32 | 124 | |
1da177e4 LT |
125 | return 0; |
126 | } | |
819d6a32 TG |
127 | EXPORT_SYMBOL(nand_calculate_ecc); |
128 | ||
129 | static inline int countbits(uint32_t byte) | |
130 | { | |
131 | int res = 0; | |
132 | ||
133 | for (;byte; byte >>= 1) | |
134 | res += byte & 0x01; | |
135 | return res; | |
136 | } | |
1da177e4 LT |
137 | |
138 | /** | |
139 | * nand_correct_data - [NAND Interface] Detect and correct bit error(s) | |
140 | * @mtd: MTD block structure | |
141 | * @dat: raw data read from the chip | |
142 | * @read_ecc: ECC from the chip | |
143 | * @calc_ecc: the ECC calculated from raw data | |
144 | * | |
145 | * Detect and correct a 1 bit error for 256 byte block | |
146 | */ | |
819d6a32 TG |
147 | int nand_correct_data(struct mtd_info *mtd, u_char *dat, |
148 | u_char *read_ecc, u_char *calc_ecc) | |
1da177e4 | 149 | { |
819d6a32 TG |
150 | uint8_t s0, s1, s2; |
151 | ||
152 | #ifdef CONFIG_NAND_ECC_SMC | |
153 | s0 = calc_ecc[0] ^ read_ecc[0]; | |
154 | s1 = calc_ecc[1] ^ read_ecc[1]; | |
155 | s2 = calc_ecc[2] ^ read_ecc[2]; | |
156 | #else | |
157 | s1 = calc_ecc[0] ^ read_ecc[0]; | |
158 | s0 = calc_ecc[1] ^ read_ecc[1]; | |
159 | s2 = calc_ecc[2] ^ read_ecc[2]; | |
160 | #endif | |
161 | if ((s0 | s1 | s2) == 0) | |
162 | return 0; | |
61b03bd7 | 163 | |
819d6a32 TG |
164 | /* Check for a single bit error */ |
165 | if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && | |
166 | ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && | |
167 | ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { | |
61b03bd7 | 168 | |
819d6a32 TG |
169 | uint32_t byteoffs, bitnum; |
170 | ||
171 | byteoffs = (s1 << 0) & 0x80; | |
172 | byteoffs |= (s1 << 1) & 0x40; | |
173 | byteoffs |= (s1 << 2) & 0x20; | |
174 | byteoffs |= (s1 << 3) & 0x10; | |
175 | ||
176 | byteoffs |= (s0 >> 4) & 0x08; | |
177 | byteoffs |= (s0 >> 3) & 0x04; | |
178 | byteoffs |= (s0 >> 2) & 0x02; | |
179 | byteoffs |= (s0 >> 1) & 0x01; | |
180 | ||
181 | bitnum = (s2 >> 5) & 0x04; | |
182 | bitnum |= (s2 >> 4) & 0x02; | |
183 | bitnum |= (s2 >> 3) & 0x01; | |
184 | ||
185 | dat[byteoffs] ^= (1 << bitnum); | |
186 | ||
187 | return 1; | |
1da177e4 | 188 | } |
61b03bd7 | 189 | |
819d6a32 TG |
190 | if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) |
191 | return 1; | |
192 | ||
1da177e4 LT |
193 | return -1; |
194 | } | |
1da177e4 LT |
195 | EXPORT_SYMBOL(nand_correct_data); |
196 | ||
197 | MODULE_LICENSE("GPL"); | |
198 | MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>"); | |
199 | MODULE_DESCRIPTION("Generic NAND ECC support"); |