*** empty log message ***
[yaffs/.git] / nand_ecc.c
1 /*
2  *  drivers/mtd/nand_ecc.c
3  *
4  *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
5  *                     Toshiba America Electronics Components, Inc.
6  *
7  * $Id: nand_ecc.c,v 1.1 2002-05-27 18:57:25 charles Exp $
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  *
13  * This file contains an ECC algorithm from Toshiba that detects and
14  * corrects 1 bit errors in a 256 byte block of data.
15  *
16  *
17  *  Slightly hacked to fit in with YAFFS by Charles Manning.
18  */
19 #if 0
20 #include <linux/types.h>
21 #include <linux/kernel.h>
22 #include <linux/module.h>
23 #endif
24
25 #include "yportenv.h"
26
27 /*
28  * Pre-calculated 256-way 1 byte column parity
29  */
30 static const u_char nand_ecc_precalc_table[] = {
31         0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
32         0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
33         0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
34         0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
35         0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
36         0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
37         0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
38         0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
39         0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
40         0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
41         0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
42         0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
43         0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
44         0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
45         0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
46         0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
47 };
48
49
50 /*
51  * Creates non-inverted ECC code from line parity
52  */
53 static void nand_trans_result(u_char reg2, u_char reg3,
54         u_char *ecc_code)
55 {
56         u_char a, b, i, tmp1, tmp2;
57         
58         /* Initialize variables */
59         a = b = 0x80;
60         tmp1 = tmp2 = 0;
61         
62         /* Calculate first ECC byte */
63         for (i = 0; i < 4; i++) {
64                 if (reg3 & a)           /* LP15,13,11,9 --> ecc_code[0] */
65                         tmp1 |= b;
66                 b >>= 1;
67                 if (reg2 & a)           /* LP14,12,10,8 --> ecc_code[0] */
68                         tmp1 |= b;
69                 b >>= 1;
70                 a >>= 1;
71         }
72         
73         /* Calculate second ECC byte */
74         b = 0x80;
75         for (i = 0; i < 4; i++) {
76                 if (reg3 & a)           /* LP7,5,3,1 --> ecc_code[1] */
77                         tmp2 |= b;
78                 b >>= 1;
79                 if (reg2 & a)           /* LP6,4,2,0 --> ecc_code[1] */
80                         tmp2 |= b;
81                 b >>= 1;
82                 a >>= 1;
83         }
84         
85         /* Store two of the ECC bytes */
86         ecc_code[0] = tmp1;
87         ecc_code[1] = tmp2;
88 }
89
90 /*
91  * Calculate 3 byte ECC code for 256 byte block
92  */
93 void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
94 {
95         u_char idx, reg1, reg2, reg3;
96         int j;
97         
98         /* Initialize variables */
99         reg1 = reg2 = reg3 = 0;
100         ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
101         
102         /* Build up column parity */ 
103         for(j = 0; j < 256; j++) {
104                 
105                 /* Get CP0 - CP5 from table */
106                 idx = nand_ecc_precalc_table[dat[j]];
107                 reg1 ^= (idx & 0x3f);
108                 
109                 /* All bit XOR = 1 ? */
110                 if (idx & 0x40) {
111                         reg3 ^= (u_char) j;
112                         reg2 ^= ~((u_char) j);
113                 }
114         }
115         
116         /* Create non-inverted ECC code from line parity */
117         nand_trans_result(reg2, reg3, ecc_code);
118         
119         /* Calculate final ECC code */
120         ecc_code[0] = ~ecc_code[0];
121         ecc_code[1] = ~ecc_code[1];
122         ecc_code[2] = ((~reg1) << 2) | 0x03;
123 }
124
125 /*
126  * Detect and correct a 1 bit error for 256 byte block
127  */
128 int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
129 {
130         u_char a, b, c, d1, d2, d3, add, bit, i;
131         
132         /* Do error detection */ 
133         d1 = calc_ecc[0] ^ read_ecc[0];
134         d2 = calc_ecc[1] ^ read_ecc[1];
135         d3 = calc_ecc[2] ^ read_ecc[2];
136         
137         if ((d1 | d2 | d3) == 0) {
138                 /* No errors */
139                 return 0;
140         }
141         else {
142                 a = (d1 ^ (d1 >> 1)) & 0x55;
143                 b = (d2 ^ (d2 >> 1)) & 0x55;
144                 c = (d3 ^ (d3 >> 1)) & 0x54;
145                 
146                 /* Found and will correct single bit error in the data */
147                 if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
148                         c = 0x80;
149                         add = 0;
150                         a = 0x80;
151                         for (i=0; i<4; i++) {
152                                 if (d1 & c)
153                                         add |= a;
154                                 c >>= 2;
155                                 a >>= 1;
156                         }
157                         c = 0x80;
158                         for (i=0; i<4; i++) {
159                                 if (d2 & c)
160                                         add |= a;
161                                 c >>= 2;
162                                 a >>= 1;
163                         }
164                         bit = 0;
165                         b = 0x04;
166                         c = 0x80;
167                         for (i=0; i<3; i++) {
168                                 if (d3 & c)
169                                         bit |= b;
170                                 c >>= 2;
171                                 b >>= 1;
172                         }
173                         b = 0x01;
174                         a = dat[add];
175                         a ^= (b << bit);
176                         dat[add] = a;
177                         return 1;
178                 }
179                 else {
180                         i = 0;
181                         while (d1) {
182                                 if (d1 & 0x01)
183                                         ++i;
184                                 d1 >>= 1;
185                         }
186                         while (d2) {
187                                 if (d2 & 0x01)
188                                         ++i;
189                                 d2 >>= 1;
190                         }
191                         while (d3) {
192                                 if (d3 & 0x01)
193                                         ++i;
194                                 d3 >>= 1;
195                         }
196                         if (i == 1) {
197                                 /* ECC Code Error Correction */
198                                 read_ecc[0] = calc_ecc[0];
199                                 read_ecc[1] = calc_ecc[1];
200                                 read_ecc[2] = calc_ecc[2];
201                                 return 2;
202                         }
203                         else {
204                                 /* Uncorrectable Error */
205                                 return -1;
206                         }
207                 }
208         }
209         
210         /* Should never happen */
211         return -1;
212 }
213
214
215 #if 0
216 EXPORT_SYMBOL(nand_calculate_ecc);
217 EXPORT_SYMBOL(nand_correct_data);
218 #endif
219