aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/nand_ecc.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-23 05:32:45 -0400
committerThomas Gleixner <tglx@cruncher.tec.linutronix.de>2006-05-23 05:32:45 -0400
commit819d6a32c397534c819d3c72a3947b7e7e4bec4b (patch)
treee5c25ca9545014a4512102629d09b42438cb378d /drivers/mtd/nand/nand_ecc.c
parenta1b563d652b54647ffacb2d6edf7859d3e97a723 (diff)
[MTD] Improve software ECC calculation
Unrolling the loops produces denser and much faster code. Add a config switch which allows to select the byte order of the resulting ecc code. The current Linux implementation has a byte swap versus the SmartMedia specification Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers/mtd/nand/nand_ecc.c')
-rw-r--r--drivers/mtd/nand/nand_ecc.c222
1 files changed, 88 insertions, 134 deletions
diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c
index 101892985b02..2a163e4084df 100644
--- a/drivers/mtd/nand/nand_ecc.c
+++ b/drivers/mtd/nand/nand_ecc.c
@@ -7,6 +7,8 @@
7 * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) 7 * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
8 * Toshiba America Electronics Components, Inc. 8 * Toshiba America Electronics Components, Inc.
9 * 9 *
10 * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
11 *
10 * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $ 12 * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $
11 * 13 *
12 * This file is free software; you can redistribute it and/or modify it 14 * This file is free software; you can redistribute it and/or modify it
@@ -63,87 +65,75 @@ static const u_char nand_ecc_precalc_table[] = {
63}; 65};
64 66
65/** 67/**
66 * nand_trans_result - [GENERIC] create non-inverted ECC 68 * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code
67 * @reg2: line parity reg 2 69 * for 256 byte block
68 * @reg3: line parity reg 3
69 * @ecc_code: ecc
70 *
71 * Creates non-inverted ECC code from line parity
72 */
73static void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code)
74{
75 u_char a, b, i, tmp1, tmp2;
76
77 /* Initialize variables */
78 a = b = 0x80;
79 tmp1 = tmp2 = 0;
80
81 /* Calculate first ECC byte */
82 for (i = 0; i < 4; i++) {
83 if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */
84 tmp1 |= b;
85 b >>= 1;
86 if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */
87 tmp1 |= b;
88 b >>= 1;
89 a >>= 1;
90 }
91
92 /* Calculate second ECC byte */
93 b = 0x80;
94 for (i = 0; i < 4; i++) {
95 if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */
96 tmp2 |= b;
97 b >>= 1;
98 if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */
99 tmp2 |= b;
100 b >>= 1;
101 a >>= 1;
102 }
103
104 /* Store two of the ECC bytes */
105 ecc_code[0] = tmp1;
106 ecc_code[1] = tmp2;
107}
108
109/**
110 * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block
111 * @mtd: MTD block structure 70 * @mtd: MTD block structure
112 * @dat: raw data 71 * @dat: raw data
113 * @ecc_code: buffer for ECC 72 * @ecc_code: buffer for ECC
114 */ 73 */
115int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) 74int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
75 u_char *ecc_code)
116{ 76{
117 u_char idx, reg1, reg2, reg3; 77 uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
118 int j; 78 int i;
119 79
120 /* Initialize variables */ 80 /* Initialize variables */
121 reg1 = reg2 = reg3 = 0; 81 reg1 = reg2 = reg3 = 0;
122 ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
123 82
124 /* Build up column parity */ 83 /* Build up column parity */
125 for (j = 0; j < 256; j++) { 84 for(i = 0; i < 256; i++) {
126
127 /* Get CP0 - CP5 from table */ 85 /* Get CP0 - CP5 from table */
128 idx = nand_ecc_precalc_table[dat[j]]; 86 idx = nand_ecc_precalc_table[*dat++];
129 reg1 ^= (idx & 0x3f); 87 reg1 ^= (idx & 0x3f);
130 88
131 /* All bit XOR = 1 ? */ 89 /* All bit XOR = 1 ? */
132 if (idx & 0x40) { 90 if (idx & 0x40) {
133 reg3 ^= (u_char) j; 91 reg3 ^= (uint8_t) i;
134 reg2 ^= ~((u_char) j); 92 reg2 ^= ~((uint8_t) i);
135 } 93 }
136 } 94 }
137 95
138 /* Create non-inverted ECC code from line parity */ 96 /* Create non-inverted ECC code from line parity */
139 nand_trans_result(reg2, reg3, ecc_code); 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 */
140 114
141 /* Calculate final ECC code */ 115 /* Calculate final ECC code */
142 ecc_code[0] = ~ecc_code[0]; 116#ifdef CONFIG_NAND_ECC_SMC
143 ecc_code[1] = ~ecc_code[1]; 117 ecc_code[0] = ~tmp2;
118 ecc_code[1] = ~tmp1;
119#else
120 ecc_code[0] = ~tmp1;
121 ecc_code[1] = ~tmp2;
122#endif
144 ecc_code[2] = ((~reg1) << 2) | 0x03; 123 ecc_code[2] = ((~reg1) << 2) | 0x03;
124
145 return 0; 125 return 0;
146} 126}
127EXPORT_SYMBOL(nand_calculate_ecc);
128
129static 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}
147 137
148/** 138/**
149 * nand_correct_data - [NAND Interface] Detect and correct bit error(s) 139 * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
@@ -154,90 +144,54 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code
154 * 144 *
155 * Detect and correct a 1 bit error for 256 byte block 145 * Detect and correct a 1 bit error for 256 byte block
156 */ 146 */
157int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) 147int nand_correct_data(struct mtd_info *mtd, u_char *dat,
148 u_char *read_ecc, u_char *calc_ecc)
158{ 149{
159 u_char a, b, c, d1, d2, d3, add, bit, i; 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;
160 163
161 /* Do error detection */ 164 /* Check for a single bit error */
162 d1 = calc_ecc[0] ^ read_ecc[0]; 165 if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
163 d2 = calc_ecc[1] ^ read_ecc[1]; 166 ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
164 d3 = calc_ecc[2] ^ read_ecc[2]; 167 ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
165 168
166 if ((d1 | d2 | d3) == 0) { 169 uint32_t byteoffs, bitnum;
167 /* No errors */ 170
168 return 0; 171 byteoffs = (s1 << 0) & 0x80;
169 } else { 172 byteoffs |= (s1 << 1) & 0x40;
170 a = (d1 ^ (d1 >> 1)) & 0x55; 173 byteoffs |= (s1 << 2) & 0x20;
171 b = (d2 ^ (d2 >> 1)) & 0x55; 174 byteoffs |= (s1 << 3) & 0x10;
172 c = (d3 ^ (d3 >> 1)) & 0x54; 175
173 176 byteoffs |= (s0 >> 4) & 0x08;
174 /* Found and will correct single bit error in the data */ 177 byteoffs |= (s0 >> 3) & 0x04;
175 if ((a == 0x55) && (b == 0x55) && (c == 0x54)) { 178 byteoffs |= (s0 >> 2) & 0x02;
176 c = 0x80; 179 byteoffs |= (s0 >> 1) & 0x01;
177 add = 0; 180
178 a = 0x80; 181 bitnum = (s2 >> 5) & 0x04;
179 for (i = 0; i < 4; i++) { 182 bitnum |= (s2 >> 4) & 0x02;
180 if (d1 & c) 183 bitnum |= (s2 >> 3) & 0x01;
181 add |= a; 184
182 c >>= 2; 185 dat[byteoffs] ^= (1 << bitnum);
183 a >>= 1; 186
184 } 187 return 1;
185 c = 0x80;
186 for (i = 0; i < 4; i++) {
187 if (d2 & c)
188 add |= a;
189 c >>= 2;
190 a >>= 1;
191 }
192 bit = 0;
193 b = 0x04;
194 c = 0x80;
195 for (i = 0; i < 3; i++) {
196 if (d3 & c)
197 bit |= b;
198 c >>= 2;
199 b >>= 1;
200 }
201 b = 0x01;
202 a = dat[add];
203 a ^= (b << bit);
204 dat[add] = a;
205 return 1;
206 } else {
207 i = 0;
208 while (d1) {
209 if (d1 & 0x01)
210 ++i;
211 d1 >>= 1;
212 }
213 while (d2) {
214 if (d2 & 0x01)
215 ++i;
216 d2 >>= 1;
217 }
218 while (d3) {
219 if (d3 & 0x01)
220 ++i;
221 d3 >>= 1;
222 }
223 if (i == 1) {
224 /* ECC Code Error Correction */
225 read_ecc[0] = calc_ecc[0];
226 read_ecc[1] = calc_ecc[1];
227 read_ecc[2] = calc_ecc[2];
228 return 2;
229 } else {
230 /* Uncorrectable Error */
231 return -1;
232 }
233 }
234 } 188 }
235 189
236 /* Should never happen */ 190 if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
191 return 1;
192
237 return -1; 193 return -1;
238} 194}
239
240EXPORT_SYMBOL(nand_calculate_ecc);
241EXPORT_SYMBOL(nand_correct_data); 195EXPORT_SYMBOL(nand_correct_data);
242 196
243MODULE_LICENSE("GPL"); 197MODULE_LICENSE("GPL");