10e8cc8bdSWilliam Juul /*
20e8cc8bdSWilliam Juul * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
30e8cc8bdSWilliam Juul *
4*753ac610SCharles Manning * Copyright (C) 2002-2011 Aleph One Ltd.
50e8cc8bdSWilliam Juul * for Toby Churchill Ltd and Brightstar Engineering
60e8cc8bdSWilliam Juul *
70e8cc8bdSWilliam Juul * Created by Charles Manning <charles@aleph1.co.uk>
80e8cc8bdSWilliam Juul *
90e8cc8bdSWilliam Juul * This program is free software; you can redistribute it and/or modify
100e8cc8bdSWilliam Juul * it under the terms of the GNU General Public License version 2 as
110e8cc8bdSWilliam Juul * published by the Free Software Foundation.
120e8cc8bdSWilliam Juul */
130e8cc8bdSWilliam Juul
140e8cc8bdSWilliam Juul /*
150e8cc8bdSWilliam Juul * This code implements the ECC algorithm used in SmartMedia.
160e8cc8bdSWilliam Juul *
170e8cc8bdSWilliam Juul * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes.
180e8cc8bdSWilliam Juul * The two unused bit are set to 1.
19*753ac610SCharles Manning * The ECC can correct single bit errors in a 256-byte page of data. Thus, two
20*753ac610SCharles Manning * such ECC blocks are used on a 512-byte NAND page.
210e8cc8bdSWilliam Juul *
220e8cc8bdSWilliam Juul */
230e8cc8bdSWilliam Juul
240e8cc8bdSWilliam Juul #include "yportenv.h"
250e8cc8bdSWilliam Juul
260e8cc8bdSWilliam Juul #include "yaffs_ecc.h"
270e8cc8bdSWilliam Juul
28*753ac610SCharles Manning /* Table generated by gen-ecc.c
29*753ac610SCharles Manning * Using a table means we do not have to calculate p1..p4 and p1'..p4'
30*753ac610SCharles Manning * for each byte of data. These are instead provided in a table in bits7..2.
31*753ac610SCharles Manning * Bit 0 of each entry indicates whether the entry has an odd or even parity,
32*753ac610SCharles Manning * and therefore this bytes influence on the line parity.
33*753ac610SCharles Manning */
34*753ac610SCharles Manning
350e8cc8bdSWilliam Juul static const unsigned char column_parity_table[] = {
360e8cc8bdSWilliam Juul 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
370e8cc8bdSWilliam Juul 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
380e8cc8bdSWilliam Juul 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
390e8cc8bdSWilliam Juul 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
400e8cc8bdSWilliam Juul 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
410e8cc8bdSWilliam Juul 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
420e8cc8bdSWilliam Juul 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
430e8cc8bdSWilliam Juul 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
440e8cc8bdSWilliam Juul 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
450e8cc8bdSWilliam Juul 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
460e8cc8bdSWilliam Juul 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
470e8cc8bdSWilliam Juul 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
480e8cc8bdSWilliam Juul 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
490e8cc8bdSWilliam Juul 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
500e8cc8bdSWilliam Juul 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
510e8cc8bdSWilliam Juul 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
520e8cc8bdSWilliam Juul 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
530e8cc8bdSWilliam Juul 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
540e8cc8bdSWilliam Juul 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
550e8cc8bdSWilliam Juul 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
560e8cc8bdSWilliam Juul 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
570e8cc8bdSWilliam Juul 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
580e8cc8bdSWilliam Juul 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
590e8cc8bdSWilliam Juul 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
600e8cc8bdSWilliam Juul 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
610e8cc8bdSWilliam Juul 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
620e8cc8bdSWilliam Juul 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
630e8cc8bdSWilliam Juul 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
640e8cc8bdSWilliam Juul 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
650e8cc8bdSWilliam Juul 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
660e8cc8bdSWilliam Juul 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
670e8cc8bdSWilliam Juul 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
680e8cc8bdSWilliam Juul };
690e8cc8bdSWilliam Juul
700e8cc8bdSWilliam Juul
710e8cc8bdSWilliam Juul /* Calculate the ECC for a 256-byte block of data */
yaffs_ecc_calc(const unsigned char * data,unsigned char * ecc)72*753ac610SCharles Manning void yaffs_ecc_calc(const unsigned char *data, unsigned char *ecc)
730e8cc8bdSWilliam Juul {
740e8cc8bdSWilliam Juul unsigned int i;
750e8cc8bdSWilliam Juul unsigned char col_parity = 0;
760e8cc8bdSWilliam Juul unsigned char line_parity = 0;
770e8cc8bdSWilliam Juul unsigned char line_parity_prime = 0;
780e8cc8bdSWilliam Juul unsigned char t;
790e8cc8bdSWilliam Juul unsigned char b;
800e8cc8bdSWilliam Juul
810e8cc8bdSWilliam Juul for (i = 0; i < 256; i++) {
820e8cc8bdSWilliam Juul b = column_parity_table[*data++];
830e8cc8bdSWilliam Juul col_parity ^= b;
840e8cc8bdSWilliam Juul
85*753ac610SCharles Manning if (b & 0x01) { /* odd number of bits in the byte */
860e8cc8bdSWilliam Juul line_parity ^= i;
870e8cc8bdSWilliam Juul line_parity_prime ^= ~i;
880e8cc8bdSWilliam Juul }
890e8cc8bdSWilliam Juul }
900e8cc8bdSWilliam Juul
910e8cc8bdSWilliam Juul ecc[2] = (~col_parity) | 0x03;
920e8cc8bdSWilliam Juul
930e8cc8bdSWilliam Juul t = 0;
940e8cc8bdSWilliam Juul if (line_parity & 0x80)
950e8cc8bdSWilliam Juul t |= 0x80;
960e8cc8bdSWilliam Juul if (line_parity_prime & 0x80)
970e8cc8bdSWilliam Juul t |= 0x40;
980e8cc8bdSWilliam Juul if (line_parity & 0x40)
990e8cc8bdSWilliam Juul t |= 0x20;
1000e8cc8bdSWilliam Juul if (line_parity_prime & 0x40)
1010e8cc8bdSWilliam Juul t |= 0x10;
1020e8cc8bdSWilliam Juul if (line_parity & 0x20)
1030e8cc8bdSWilliam Juul t |= 0x08;
1040e8cc8bdSWilliam Juul if (line_parity_prime & 0x20)
1050e8cc8bdSWilliam Juul t |= 0x04;
1060e8cc8bdSWilliam Juul if (line_parity & 0x10)
1070e8cc8bdSWilliam Juul t |= 0x02;
1080e8cc8bdSWilliam Juul if (line_parity_prime & 0x10)
1090e8cc8bdSWilliam Juul t |= 0x01;
1100e8cc8bdSWilliam Juul ecc[1] = ~t;
1110e8cc8bdSWilliam Juul
1120e8cc8bdSWilliam Juul t = 0;
1130e8cc8bdSWilliam Juul if (line_parity & 0x08)
1140e8cc8bdSWilliam Juul t |= 0x80;
1150e8cc8bdSWilliam Juul if (line_parity_prime & 0x08)
1160e8cc8bdSWilliam Juul t |= 0x40;
1170e8cc8bdSWilliam Juul if (line_parity & 0x04)
1180e8cc8bdSWilliam Juul t |= 0x20;
1190e8cc8bdSWilliam Juul if (line_parity_prime & 0x04)
1200e8cc8bdSWilliam Juul t |= 0x10;
1210e8cc8bdSWilliam Juul if (line_parity & 0x02)
1220e8cc8bdSWilliam Juul t |= 0x08;
1230e8cc8bdSWilliam Juul if (line_parity_prime & 0x02)
1240e8cc8bdSWilliam Juul t |= 0x04;
1250e8cc8bdSWilliam Juul if (line_parity & 0x01)
1260e8cc8bdSWilliam Juul t |= 0x02;
1270e8cc8bdSWilliam Juul if (line_parity_prime & 0x01)
1280e8cc8bdSWilliam Juul t |= 0x01;
1290e8cc8bdSWilliam Juul ecc[0] = ~t;
1300e8cc8bdSWilliam Juul
1310e8cc8bdSWilliam Juul }
1320e8cc8bdSWilliam Juul
1330e8cc8bdSWilliam Juul /* Correct the ECC on a 256 byte block of data */
1340e8cc8bdSWilliam Juul
yaffs_ecc_correct(unsigned char * data,unsigned char * read_ecc,const unsigned char * test_ecc)135*753ac610SCharles Manning int yaffs_ecc_correct(unsigned char *data, unsigned char *read_ecc,
1360e8cc8bdSWilliam Juul const unsigned char *test_ecc)
1370e8cc8bdSWilliam Juul {
1380e8cc8bdSWilliam Juul unsigned char d0, d1, d2; /* deltas */
1390e8cc8bdSWilliam Juul
1400e8cc8bdSWilliam Juul d0 = read_ecc[0] ^ test_ecc[0];
1410e8cc8bdSWilliam Juul d1 = read_ecc[1] ^ test_ecc[1];
1420e8cc8bdSWilliam Juul d2 = read_ecc[2] ^ test_ecc[2];
1430e8cc8bdSWilliam Juul
1440e8cc8bdSWilliam Juul if ((d0 | d1 | d2) == 0)
1450e8cc8bdSWilliam Juul return 0; /* no error */
1460e8cc8bdSWilliam Juul
1470e8cc8bdSWilliam Juul if (((d0 ^ (d0 >> 1)) & 0x55) == 0x55 &&
1480e8cc8bdSWilliam Juul ((d1 ^ (d1 >> 1)) & 0x55) == 0x55 &&
1490e8cc8bdSWilliam Juul ((d2 ^ (d2 >> 1)) & 0x54) == 0x54) {
1500e8cc8bdSWilliam Juul /* Single bit (recoverable) error in data */
1510e8cc8bdSWilliam Juul
1520e8cc8bdSWilliam Juul unsigned byte;
1530e8cc8bdSWilliam Juul unsigned bit;
1540e8cc8bdSWilliam Juul
1550e8cc8bdSWilliam Juul bit = byte = 0;
1560e8cc8bdSWilliam Juul
1570e8cc8bdSWilliam Juul if (d1 & 0x80)
1580e8cc8bdSWilliam Juul byte |= 0x80;
1590e8cc8bdSWilliam Juul if (d1 & 0x20)
1600e8cc8bdSWilliam Juul byte |= 0x40;
1610e8cc8bdSWilliam Juul if (d1 & 0x08)
1620e8cc8bdSWilliam Juul byte |= 0x20;
1630e8cc8bdSWilliam Juul if (d1 & 0x02)
1640e8cc8bdSWilliam Juul byte |= 0x10;
1650e8cc8bdSWilliam Juul if (d0 & 0x80)
1660e8cc8bdSWilliam Juul byte |= 0x08;
1670e8cc8bdSWilliam Juul if (d0 & 0x20)
1680e8cc8bdSWilliam Juul byte |= 0x04;
1690e8cc8bdSWilliam Juul if (d0 & 0x08)
1700e8cc8bdSWilliam Juul byte |= 0x02;
1710e8cc8bdSWilliam Juul if (d0 & 0x02)
1720e8cc8bdSWilliam Juul byte |= 0x01;
1730e8cc8bdSWilliam Juul
1740e8cc8bdSWilliam Juul if (d2 & 0x80)
1750e8cc8bdSWilliam Juul bit |= 0x04;
1760e8cc8bdSWilliam Juul if (d2 & 0x20)
1770e8cc8bdSWilliam Juul bit |= 0x02;
1780e8cc8bdSWilliam Juul if (d2 & 0x08)
1790e8cc8bdSWilliam Juul bit |= 0x01;
1800e8cc8bdSWilliam Juul
1810e8cc8bdSWilliam Juul data[byte] ^= (1 << bit);
1820e8cc8bdSWilliam Juul
1830e8cc8bdSWilliam Juul return 1; /* Corrected the error */
1840e8cc8bdSWilliam Juul }
1850e8cc8bdSWilliam Juul
186*753ac610SCharles Manning if ((hweight8(d0) + hweight8(d1) + hweight8(d2)) == 1) {
1870e8cc8bdSWilliam Juul /* Reccoverable error in ecc */
1880e8cc8bdSWilliam Juul
1890e8cc8bdSWilliam Juul read_ecc[0] = test_ecc[0];
1900e8cc8bdSWilliam Juul read_ecc[1] = test_ecc[1];
1910e8cc8bdSWilliam Juul read_ecc[2] = test_ecc[2];
1920e8cc8bdSWilliam Juul
1930e8cc8bdSWilliam Juul return 1; /* Corrected the error */
1940e8cc8bdSWilliam Juul }
1950e8cc8bdSWilliam Juul
1960e8cc8bdSWilliam Juul /* Unrecoverable error */
1970e8cc8bdSWilliam Juul
1980e8cc8bdSWilliam Juul return -1;
1990e8cc8bdSWilliam Juul
2000e8cc8bdSWilliam Juul }
2010e8cc8bdSWilliam Juul
2020e8cc8bdSWilliam Juul /*
2030e8cc8bdSWilliam Juul * ECCxxxOther does ECC calcs on arbitrary n bytes of data
2040e8cc8bdSWilliam Juul */
yaffs_ecc_calc_other(const unsigned char * data,unsigned n_bytes,struct yaffs_ecc_other * ecc_other)205*753ac610SCharles Manning void yaffs_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
206*753ac610SCharles Manning struct yaffs_ecc_other *ecc_other)
2070e8cc8bdSWilliam Juul {
2080e8cc8bdSWilliam Juul unsigned int i;
2090e8cc8bdSWilliam Juul unsigned char col_parity = 0;
2100e8cc8bdSWilliam Juul unsigned line_parity = 0;
2110e8cc8bdSWilliam Juul unsigned line_parity_prime = 0;
2120e8cc8bdSWilliam Juul unsigned char b;
2130e8cc8bdSWilliam Juul
214*753ac610SCharles Manning for (i = 0; i < n_bytes; i++) {
2150e8cc8bdSWilliam Juul b = column_parity_table[*data++];
2160e8cc8bdSWilliam Juul col_parity ^= b;
2170e8cc8bdSWilliam Juul
2180e8cc8bdSWilliam Juul if (b & 0x01) {
2190e8cc8bdSWilliam Juul /* odd number of bits in the byte */
2200e8cc8bdSWilliam Juul line_parity ^= i;
2210e8cc8bdSWilliam Juul line_parity_prime ^= ~i;
2220e8cc8bdSWilliam Juul }
2230e8cc8bdSWilliam Juul
2240e8cc8bdSWilliam Juul }
2250e8cc8bdSWilliam Juul
226*753ac610SCharles Manning ecc_other->col_parity = (col_parity >> 2) & 0x3f;
227*753ac610SCharles Manning ecc_other->line_parity = line_parity;
228*753ac610SCharles Manning ecc_other->line_parity_prime = line_parity_prime;
2290e8cc8bdSWilliam Juul }
2300e8cc8bdSWilliam Juul
yaffs_ecc_correct_other(unsigned char * data,unsigned n_bytes,struct yaffs_ecc_other * read_ecc,const struct yaffs_ecc_other * test_ecc)231*753ac610SCharles Manning int yaffs_ecc_correct_other(unsigned char *data, unsigned n_bytes,
232*753ac610SCharles Manning struct yaffs_ecc_other *read_ecc,
233*753ac610SCharles Manning const struct yaffs_ecc_other *test_ecc)
2340e8cc8bdSWilliam Juul {
235*753ac610SCharles Manning unsigned char delta_col; /* column parity delta */
236*753ac610SCharles Manning unsigned delta_line; /* line parity delta */
237*753ac610SCharles Manning unsigned delta_line_prime; /* line parity delta */
2380e8cc8bdSWilliam Juul unsigned bit;
2390e8cc8bdSWilliam Juul
240*753ac610SCharles Manning delta_col = read_ecc->col_parity ^ test_ecc->col_parity;
241*753ac610SCharles Manning delta_line = read_ecc->line_parity ^ test_ecc->line_parity;
242*753ac610SCharles Manning delta_line_prime =
243*753ac610SCharles Manning read_ecc->line_parity_prime ^ test_ecc->line_parity_prime;
2440e8cc8bdSWilliam Juul
245*753ac610SCharles Manning if ((delta_col | delta_line | delta_line_prime) == 0)
2460e8cc8bdSWilliam Juul return 0; /* no error */
2470e8cc8bdSWilliam Juul
248*753ac610SCharles Manning if (delta_line == ~delta_line_prime &&
249*753ac610SCharles Manning (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) {
2500e8cc8bdSWilliam Juul /* Single bit (recoverable) error in data */
2510e8cc8bdSWilliam Juul
2520e8cc8bdSWilliam Juul bit = 0;
2530e8cc8bdSWilliam Juul
254*753ac610SCharles Manning if (delta_col & 0x20)
2550e8cc8bdSWilliam Juul bit |= 0x04;
256*753ac610SCharles Manning if (delta_col & 0x08)
2570e8cc8bdSWilliam Juul bit |= 0x02;
258*753ac610SCharles Manning if (delta_col & 0x02)
2590e8cc8bdSWilliam Juul bit |= 0x01;
2600e8cc8bdSWilliam Juul
261*753ac610SCharles Manning if (delta_line >= n_bytes)
2620e8cc8bdSWilliam Juul return -1;
2630e8cc8bdSWilliam Juul
264*753ac610SCharles Manning data[delta_line] ^= (1 << bit);
2650e8cc8bdSWilliam Juul
2660e8cc8bdSWilliam Juul return 1; /* corrected */
2670e8cc8bdSWilliam Juul }
2680e8cc8bdSWilliam Juul
269*753ac610SCharles Manning if ((hweight32(delta_line) +
270*753ac610SCharles Manning hweight32(delta_line_prime) +
271*753ac610SCharles Manning hweight8(delta_col)) == 1) {
2720e8cc8bdSWilliam Juul /* Reccoverable error in ecc */
2730e8cc8bdSWilliam Juul
2740e8cc8bdSWilliam Juul *read_ecc = *test_ecc;
2750e8cc8bdSWilliam Juul return 1; /* corrected */
2760e8cc8bdSWilliam Juul }
2770e8cc8bdSWilliam Juul
2780e8cc8bdSWilliam Juul /* Unrecoverable error */
2790e8cc8bdSWilliam Juul
2800e8cc8bdSWilliam Juul return -1;
2810e8cc8bdSWilliam Juul }
282