blob: 0860a3c7843107a7df6f16beee801a21e974e922 [file] [log] [blame]
William Juulc051bbe2007-11-15 11:13:05 +01001/*
2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3 *
Charles Manning3796e1f2012-05-09 16:55:17 +00004 * Copyright (C) 2002-2011 Aleph One Ltd.
William Juulc051bbe2007-11-15 11:13:05 +01005 * for Toby Churchill Ltd and Brightstar Engineering
6 *
7 * Created by Charles Manning <charles@aleph1.co.uk>
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
14/*
15 * This code implements the ECC algorithm used in SmartMedia.
16 *
Wolfgang Denk74e0dde2008-08-14 14:41:06 +020017 * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
William Juulc051bbe2007-11-15 11:13:05 +010018 * The two unused bit are set to 1.
Charles Manning3796e1f2012-05-09 16:55:17 +000019 * The ECC can correct single bit errors in a 256-byte page of data. Thus, two
20 * such ECC blocks are used on a 512-byte NAND page.
William Juulc051bbe2007-11-15 11:13:05 +010021 *
22 */
23
Charles Manning3796e1f2012-05-09 16:55:17 +000024#include "yportenv.h"
25
26#include "yaffs_ecc.h"
27
William Juulc051bbe2007-11-15 11:13:05 +010028/* Table generated by gen-ecc.c
29 * Using a table means we do not have to calculate p1..p4 and p1'..p4'
30 * for each byte of data. These are instead provided in a table in bits7..2.
Charles Manning3796e1f2012-05-09 16:55:17 +000031 * Bit 0 of each entry indicates whether the entry has an odd or even parity,
32 * and therefore this bytes influence on the line parity.
William Juulc051bbe2007-11-15 11:13:05 +010033 */
34
William Juulc051bbe2007-11-15 11:13:05 +010035static const unsigned char column_parity_table[] = {
36 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
37 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
38 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
39 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
40 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
41 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
42 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
43 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
44 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
45 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
46 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
47 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
48 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
49 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
50 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
51 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
52 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
53 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
54 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
55 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
56 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
57 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
58 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
59 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
60 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
61 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
62 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
63 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
64 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
65 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
66 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
67 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
68};
69
William Juulc051bbe2007-11-15 11:13:05 +010070/* Calculate the ECC for a 256-byte block of data */
Charles Manning3796e1f2012-05-09 16:55:17 +000071void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc)
William Juulc051bbe2007-11-15 11:13:05 +010072{
73 unsigned int i;
William Juulc051bbe2007-11-15 11:13:05 +010074 unsigned char col_parity = 0;
75 unsigned char line_parity = 0;
76 unsigned char line_parity_prime = 0;
77 unsigned char t;
78 unsigned char b;
79
80 for (i = 0; i < 256; i++) {
81 b = column_parity_table[*data++];
82 col_parity ^= b;
83
Charles Manning3796e1f2012-05-09 16:55:17 +000084 if (b & 0x01) { /* odd number of bits in the byte */
William Juulc051bbe2007-11-15 11:13:05 +010085 line_parity ^= i;
86 line_parity_prime ^= ~i;
87 }
William Juulc051bbe2007-11-15 11:13:05 +010088 }
89
90 ecc[2] = (~col_parity) | 0x03;
91
92 t = 0;
93 if (line_parity & 0x80)
94 t |= 0x80;
95 if (line_parity_prime & 0x80)
96 t |= 0x40;
97 if (line_parity & 0x40)
98 t |= 0x20;
99 if (line_parity_prime & 0x40)
100 t |= 0x10;
101 if (line_parity & 0x20)
102 t |= 0x08;
103 if (line_parity_prime & 0x20)
104 t |= 0x04;
105 if (line_parity & 0x10)
106 t |= 0x02;
107 if (line_parity_prime & 0x10)
108 t |= 0x01;
109 ecc[1] = ~t;
110
111 t = 0;
112 if (line_parity & 0x08)
113 t |= 0x80;
114 if (line_parity_prime & 0x08)
115 t |= 0x40;
116 if (line_parity & 0x04)
117 t |= 0x20;
118 if (line_parity_prime & 0x04)
119 t |= 0x10;
120 if (line_parity & 0x02)
121 t |= 0x08;
122 if (line_parity_prime & 0x02)
123 t |= 0x04;
124 if (line_parity & 0x01)
125 t |= 0x02;
126 if (line_parity_prime & 0x01)
127 t |= 0x01;
128 ecc[0] = ~t;
129
William Juulc051bbe2007-11-15 11:13:05 +0100130}
131
William Juulc051bbe2007-11-15 11:13:05 +0100132/* Correct the ECC on a 256 byte block of data */
133
Charles Manning3796e1f2012-05-09 16:55:17 +0000134int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
135 const unsigned char *test_ecc)
William Juulc051bbe2007-11-15 11:13:05 +0100136{
137 unsigned char d0, d1, d2; /* deltas */
138
139 d0 = read_ecc[0] ^ test_ecc[0];
140 d1 = read_ecc[1] ^ test_ecc[1];
141 d2 = read_ecc[2] ^ test_ecc[2];
142
143 if ((d0 | d1 | d2) == 0)
Charles Manning3796e1f2012-05-09 16:55:17 +0000144 return 0; /* no error */
William Juulc051bbe2007-11-15 11:13:05 +0100145
146 if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
147 ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
148 ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) {
149 /* Single bit (recoverable) error in data */
150
151 unsigned byte;
152 unsigned bit;
153
William Juulc051bbe2007-11-15 11:13:05 +0100154 bit = byte = 0;
155
156 if (d1 & 0x80)
157 byte |= 0x80;
158 if (d1 & 0x20)
159 byte |= 0x40;
160 if (d1 & 0x08)
161 byte |= 0x20;
162 if (d1 & 0x02)
163 byte |= 0x10;
164 if (d0 & 0x80)
165 byte |= 0x08;
166 if (d0 & 0x20)
167 byte |= 0x04;
168 if (d0 & 0x08)
169 byte |= 0x02;
170 if (d0 & 0x02)
171 byte |= 0x01;
172
173 if (d2 & 0x80)
174 bit |= 0x04;
175 if (d2 & 0x20)
176 bit |= 0x02;
177 if (d2 & 0x08)
178 bit |= 0x01;
179
180 data[byte] ^= (1 << bit);
181
Charles Manning3796e1f2012-05-09 16:55:17 +0000182 return 1; /* Corrected the error */
William Juulc051bbe2007-11-15 11:13:05 +0100183 }
184
Charles Manning3796e1f2012-05-09 16:55:17 +0000185 if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) {
William Juulc051bbe2007-11-15 11:13:05 +0100186 /* Reccoverable error in ecc */
187
188 read_ecc[0] = test_ecc[0];
189 read_ecc[1] = test_ecc[1];
190 read_ecc[2] = test_ecc[2];
191
Charles Manning3796e1f2012-05-09 16:55:17 +0000192 return 1; /* Corrected the error */
William Juulc051bbe2007-11-15 11:13:05 +0100193 }
Wolfgang Denk74e0dde2008-08-14 14:41:06 +0200194
William Juulc051bbe2007-11-15 11:13:05 +0100195 /* Unrecoverable error */
196
197 return -1;
198
199}
200
William Juulc051bbe2007-11-15 11:13:05 +0100201/*
202 * ECCxxxOther does ECC calcs on arbitrary n bytes of data
203 */
Charles Manning3796e1f2012-05-09 16:55:17 +0000204void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
205 struct yaffs_ecc_other *ecc_other)
William Juulc051bbe2007-11-15 11:13:05 +0100206{
207 unsigned int i;
William Juulc051bbe2007-11-15 11:13:05 +0100208 unsigned char col_parity = 0;
209 unsigned line_parity = 0;
210 unsigned line_parity_prime = 0;
211 unsigned char b;
212
Charles Manning3796e1f2012-05-09 16:55:17 +0000213 for (i = 0; i < n_bytes; i++) {
William Juulc051bbe2007-11-15 11:13:05 +0100214 b = column_parity_table[*data++];
215 col_parity ^= b;
216
Charles Manning3796e1f2012-05-09 16:55:17 +0000217 if (b & 0x01) {
William Juulc051bbe2007-11-15 11:13:05 +0100218 /* odd number of bits in the byte */
219 line_parity ^= i;
220 line_parity_prime ^= ~i;
221 }
222
223 }
224
Charles Manning3796e1f2012-05-09 16:55:17 +0000225 ecc_other->col_parity = (col_parity >> 2) & 0x3f;
226 ecc_other->line_parity = line_parity;
227 ecc_other->line_parity_prime = line_parity_prime;
William Juulc051bbe2007-11-15 11:13:05 +0100228}
229
Charles Manning3796e1f2012-05-09 16:55:17 +0000230int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
231 struct yaffs_ecc_other *read_ecc,
232 const struct yaffs_ecc_other *test_ecc)
William Juulc051bbe2007-11-15 11:13:05 +0100233{
Charles Manning3796e1f2012-05-09 16:55:17 +0000234 unsigned char delta_col; /* column parity delta */
235 unsigned delta_line; /* line parity delta */
236 unsigned delta_line_prime; /* line parity delta */
William Juulc051bbe2007-11-15 11:13:05 +0100237 unsigned bit;
238
Charles Manning3796e1f2012-05-09 16:55:17 +0000239 delta_col = read_ecc->col_parity ^ test_ecc->col_parity;
240 delta_line = read_ecc->line_parity ^ test_ecc->line_parity;
241 delta_line_prime =
242 read_ecc->line_parity_prime ^ test_ecc->line_parity_prime;
William Juulc051bbe2007-11-15 11:13:05 +0100243
Charles Manning3796e1f2012-05-09 16:55:17 +0000244 if ((delta_col | delta_line | delta_line_prime) == 0)
245 return 0; /* no error */
William Juulc051bbe2007-11-15 11:13:05 +0100246
Charles Manning3796e1f2012-05-09 16:55:17 +0000247 if (delta_line == ~delta_line_prime &&
248 (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) {
William Juulc051bbe2007-11-15 11:13:05 +0100249 /* Single bit (recoverable) error in data */
250
251 bit = 0;
252
Charles Manning3796e1f2012-05-09 16:55:17 +0000253 if (delta_col & 0x20)
William Juulc051bbe2007-11-15 11:13:05 +0100254 bit |= 0x04;
Charles Manning3796e1f2012-05-09 16:55:17 +0000255 if (delta_col & 0x08)
William Juulc051bbe2007-11-15 11:13:05 +0100256 bit |= 0x02;
Charles Manning3796e1f2012-05-09 16:55:17 +0000257 if (delta_col & 0x02)
William Juulc051bbe2007-11-15 11:13:05 +0100258 bit |= 0x01;
259
Charles Manning3796e1f2012-05-09 16:55:17 +0000260 if (delta_line >= n_bytes)
William Juulc051bbe2007-11-15 11:13:05 +0100261 return -1;
Wolfgang Denk74e0dde2008-08-14 14:41:06 +0200262
Charles Manning3796e1f2012-05-09 16:55:17 +0000263 data[delta_line] ^= (1 << bit);
William Juulc051bbe2007-11-15 11:13:05 +0100264
Charles Manning3796e1f2012-05-09 16:55:17 +0000265 return 1; /* corrected */
William Juulc051bbe2007-11-15 11:13:05 +0100266 }
267
Charles Manning3796e1f2012-05-09 16:55:17 +0000268 if ((hweight32(delta_line) +
269 hweight32(delta_line_prime) +
270 hweight8(delta_col)) == 1) {
William Juulc051bbe2007-11-15 11:13:05 +0100271 /* Reccoverable error in ecc */
272
273 *read_ecc = *test_ecc;
Charles Manning3796e1f2012-05-09 16:55:17 +0000274 return 1; /* corrected */
William Juulc051bbe2007-11-15 11:13:05 +0100275 }
276
277 /* Unrecoverable error */
278
279 return -1;
William Juulc051bbe2007-11-15 11:13:05 +0100280}