xref: /openbmc/u-boot/fs/yaffs2/yaffs_ecc.c (revision 753ac610880e6e563d0384bb114f8b41df89e520)
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