]>
Commit | Line | Data |
---|---|---|
932394ac WD |
1 | /* |
2 | * This file contains an ECC algorithm from Toshiba that detects and | |
3 | * corrects 1 bit errors in a 256 byte block of data. | |
4 | * | |
5 | * drivers/mtd/nand/nand_ecc.c | |
6 | * | |
7 | * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) | |
8 | * Toshiba America Electronics Components, Inc. | |
9 | * | |
cfa460ad WJ |
10 | * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de> |
11 | * | |
1a459660 | 12 | * SPDX-License-Identifier: GPL-2.0+ |
ac7eb8a3 | 13 | * |
932394ac WD |
14 | * As a special exception, if other files instantiate templates or use |
15 | * macros or inline functions from these files, or you compile these | |
16 | * files and link them with other works to produce a work based on these | |
17 | * files, these files do not by themselves cause the resulting work to be | |
18 | * covered by the GNU General Public License. However the source code for | |
19 | * these files must still be made available in accordance with section (3) | |
20 | * of the GNU General Public License. | |
ac7eb8a3 | 21 | * |
932394ac WD |
22 | * This exception does not invalidate any other reasons why a work based on |
23 | * this file might be covered by the GNU General Public License. | |
24 | */ | |
25 | ||
26 | #include <common.h> | |
27 | ||
c45912d8 SW |
28 | #include <asm/errno.h> |
29 | #include <linux/mtd/mtd.h> | |
7d2ab9ae | 30 | #include <linux/mtd/nand_ecc.h> |
17b5e862 | 31 | |
f40f6db2 SR |
32 | /* The PPC4xx NDFC uses Smart Media (SMC) bytes order */ |
33 | #ifdef CONFIG_NAND_NDFC | |
34 | #define CONFIG_MTD_NAND_ECC_SMC | |
35 | #endif | |
36 | ||
17b5e862 SR |
37 | /* |
38 | * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(), | |
39 | * only nand_correct_data() is needed | |
40 | */ | |
41 | ||
1df308e5 | 42 | #if !defined(CONFIG_NAND_SPL) || defined(CONFIG_SPL_NAND_SOFTECC) |
932394ac WD |
43 | /* |
44 | * Pre-calculated 256-way 1 byte column parity | |
45 | */ | |
46 | static const u_char nand_ecc_precalc_table[] = { | |
47 | 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, | |
48 | 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, | |
49 | 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, | |
50 | 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, | |
51 | 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, | |
52 | 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, | |
53 | 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, | |
54 | 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, | |
55 | 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, | |
56 | 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, | |
57 | 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, | |
58 | 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, | |
59 | 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, | |
60 | 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, | |
61 | 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, | |
62 | 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 | |
63 | }; | |
64 | ||
932394ac | 65 | /** |
17b5e862 | 66 | * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block |
932394ac WD |
67 | * @mtd: MTD block structure |
68 | * @dat: raw data | |
69 | * @ecc_code: buffer for ECC | |
70 | */ | |
17b5e862 SR |
71 | int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, |
72 | u_char *ecc_code) | |
932394ac | 73 | { |
17b5e862 SR |
74 | uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; |
75 | int i; | |
ac7eb8a3 | 76 | |
932394ac WD |
77 | /* Initialize variables */ |
78 | reg1 = reg2 = reg3 = 0; | |
ac7eb8a3 WD |
79 | |
80 | /* Build up column parity */ | |
17b5e862 | 81 | for(i = 0; i < 256; i++) { |
932394ac | 82 | /* Get CP0 - CP5 from table */ |
17b5e862 | 83 | idx = nand_ecc_precalc_table[*dat++]; |
932394ac | 84 | reg1 ^= (idx & 0x3f); |
ac7eb8a3 | 85 | |
932394ac WD |
86 | /* All bit XOR = 1 ? */ |
87 | if (idx & 0x40) { | |
17b5e862 SR |
88 | reg3 ^= (uint8_t) i; |
89 | reg2 ^= ~((uint8_t) i); | |
932394ac WD |
90 | } |
91 | } | |
ac7eb8a3 | 92 | |
932394ac | 93 | /* Create non-inverted ECC code from line parity */ |
17b5e862 SR |
94 | tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ |
95 | tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ | |
96 | tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ | |
97 | tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ | |
98 | tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ | |
99 | tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ | |
100 | tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ | |
101 | tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ | |
102 | ||
103 | tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ | |
104 | tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ | |
105 | tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ | |
106 | tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ | |
107 | tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ | |
108 | tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ | |
109 | tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ | |
110 | tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ | |
ac7eb8a3 | 111 | |
932394ac | 112 | /* Calculate final ECC code */ |
17b5e862 SR |
113 | #ifdef CONFIG_MTD_NAND_ECC_SMC |
114 | ecc_code[0] = ~tmp2; | |
115 | ecc_code[1] = ~tmp1; | |
116 | #else | |
117 | ecc_code[0] = ~tmp1; | |
118 | ecc_code[1] = ~tmp2; | |
119 | #endif | |
932394ac | 120 | ecc_code[2] = ((~reg1) << 2) | 0x03; |
17b5e862 | 121 | |
932394ac WD |
122 | return 0; |
123 | } | |
17b5e862 SR |
124 | #endif /* CONFIG_NAND_SPL */ |
125 | ||
126 | static inline int countbits(uint32_t byte) | |
127 | { | |
128 | int res = 0; | |
129 | ||
130 | for (;byte; byte >>= 1) | |
131 | res += byte & 0x01; | |
132 | return res; | |
133 | } | |
932394ac WD |
134 | |
135 | /** | |
136 | * nand_correct_data - [NAND Interface] Detect and correct bit error(s) | |
137 | * @mtd: MTD block structure | |
138 | * @dat: raw data read from the chip | |
139 | * @read_ecc: ECC from the chip | |
140 | * @calc_ecc: the ECC calculated from raw data | |
141 | * | |
142 | * Detect and correct a 1 bit error for 256 byte block | |
143 | */ | |
17b5e862 SR |
144 | int nand_correct_data(struct mtd_info *mtd, u_char *dat, |
145 | u_char *read_ecc, u_char *calc_ecc) | |
932394ac | 146 | { |
17b5e862 SR |
147 | uint8_t s0, s1, s2; |
148 | ||
149 | #ifdef CONFIG_MTD_NAND_ECC_SMC | |
150 | s0 = calc_ecc[0] ^ read_ecc[0]; | |
151 | s1 = calc_ecc[1] ^ read_ecc[1]; | |
152 | s2 = calc_ecc[2] ^ read_ecc[2]; | |
153 | #else | |
154 | s1 = calc_ecc[0] ^ read_ecc[0]; | |
155 | s0 = calc_ecc[1] ^ read_ecc[1]; | |
156 | s2 = calc_ecc[2] ^ read_ecc[2]; | |
157 | #endif | |
158 | if ((s0 | s1 | s2) == 0) | |
159 | return 0; | |
ac7eb8a3 | 160 | |
17b5e862 SR |
161 | /* Check for a single bit error */ |
162 | if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && | |
163 | ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && | |
164 | ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { | |
ac7eb8a3 | 165 | |
17b5e862 SR |
166 | uint32_t byteoffs, bitnum; |
167 | ||
168 | byteoffs = (s1 << 0) & 0x80; | |
169 | byteoffs |= (s1 << 1) & 0x40; | |
170 | byteoffs |= (s1 << 2) & 0x20; | |
171 | byteoffs |= (s1 << 3) & 0x10; | |
172 | ||
173 | byteoffs |= (s0 >> 4) & 0x08; | |
174 | byteoffs |= (s0 >> 3) & 0x04; | |
175 | byteoffs |= (s0 >> 2) & 0x02; | |
176 | byteoffs |= (s0 >> 1) & 0x01; | |
177 | ||
178 | bitnum = (s2 >> 5) & 0x04; | |
179 | bitnum |= (s2 >> 4) & 0x02; | |
180 | bitnum |= (s2 >> 3) & 0x01; | |
181 | ||
182 | dat[byteoffs] ^= (1 << bitnum); | |
183 | ||
184 | return 1; | |
932394ac | 185 | } |
ac7eb8a3 | 186 | |
17b5e862 SR |
187 | if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) |
188 | return 1; | |
189 | ||
c45912d8 | 190 | return -EBADMSG; |
932394ac | 191 | } |