Commit | Line | Data |
---|---|---|
266dead2 LHC |
1 | /***************************************************************************** |
2 | * Copyright 2003 - 2009 Broadcom Corporation. All rights reserved. | |
3 | * | |
4 | * Unless you and Broadcom execute a separate written software license | |
5 | * agreement governing use of this software, this software is licensed to you | |
6 | * under the terms of the GNU General Public License version 2, available at | |
7 | * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). | |
8 | * | |
9 | * Notwithstanding the above, under no circumstances may you combine this | |
10 | * software in any way with any other Broadcom software provided under a | |
11 | * license other than the GPL, without Broadcom's express prior written | |
12 | * consent. | |
13 | *****************************************************************************/ | |
14 | #ifndef NAND_BCM_UMI_H | |
15 | #define NAND_BCM_UMI_H | |
16 | ||
17 | /* ---- Include Files ---------------------------------------------------- */ | |
18 | #include <mach/reg_umi.h> | |
19 | #include <mach/reg_nand.h> | |
20 | #include <cfg_global.h> | |
21 | ||
22 | /* ---- Constants and Types ---------------------------------------------- */ | |
23 | #if (CFG_GLOBAL_CHIP_FAMILY == CFG_GLOBAL_CHIP_FAMILY_BCMRING) | |
24 | #define NAND_ECC_BCH (CFG_GLOBAL_CHIP_REV > 0xA0) | |
25 | #else | |
26 | #define NAND_ECC_BCH 0 | |
27 | #endif | |
28 | ||
29 | #define CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES 13 | |
30 | ||
31 | #if NAND_ECC_BCH | |
32 | #ifdef BOOT0_BUILD | |
33 | #define NAND_ECC_NUM_BYTES 13 | |
34 | #else | |
35 | #define NAND_ECC_NUM_BYTES CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES | |
36 | #endif | |
37 | #else | |
38 | #define NAND_ECC_NUM_BYTES 3 | |
39 | #endif | |
40 | ||
41 | #define NAND_DATA_ACCESS_SIZE 512 | |
42 | ||
43 | /* ---- Variable Externs ------------------------------------------ */ | |
44 | /* ---- Function Prototypes --------------------------------------- */ | |
45 | int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData, | |
46 | int numEccBytes); | |
47 | ||
48 | /* Check in device is ready */ | |
49 | static inline int nand_bcm_umi_dev_ready(void) | |
50 | { | |
51 | return REG_UMI_NAND_RCSR & REG_UMI_NAND_RCSR_RDY; | |
52 | } | |
53 | ||
54 | /* Wait until device is ready */ | |
55 | static inline void nand_bcm_umi_wait_till_ready(void) | |
56 | { | |
57 | while (nand_bcm_umi_dev_ready() == 0) | |
58 | ; | |
59 | } | |
60 | ||
61 | /* Enable Hamming ECC */ | |
62 | static inline void nand_bcm_umi_hamming_enable_hwecc(void) | |
63 | { | |
64 | /* disable and reset ECC, 512 byte page */ | |
65 | REG_UMI_NAND_ECC_CSR &= ~(REG_UMI_NAND_ECC_CSR_ECC_ENABLE | | |
66 | REG_UMI_NAND_ECC_CSR_256BYTE); | |
67 | /* enable ECC */ | |
68 | REG_UMI_NAND_ECC_CSR |= REG_UMI_NAND_ECC_CSR_ECC_ENABLE; | |
69 | } | |
70 | ||
71 | #if NAND_ECC_BCH | |
72 | /* BCH ECC specifics */ | |
73 | #define ECC_BITS_PER_CORRECTABLE_BIT 13 | |
74 | ||
75 | /* Enable BCH Read ECC */ | |
76 | static inline void nand_bcm_umi_bch_enable_read_hwecc(void) | |
77 | { | |
78 | /* disable and reset ECC */ | |
79 | REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID; | |
80 | /* Turn on ECC */ | |
81 | REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN; | |
82 | } | |
83 | ||
84 | /* Enable BCH Write ECC */ | |
85 | static inline void nand_bcm_umi_bch_enable_write_hwecc(void) | |
86 | { | |
87 | /* disable and reset ECC */ | |
88 | REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID; | |
89 | /* Turn on ECC */ | |
90 | REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN; | |
91 | } | |
92 | ||
93 | /* Config number of BCH ECC bytes */ | |
94 | static inline void nand_bcm_umi_bch_config_ecc(uint8_t numEccBytes) | |
95 | { | |
96 | uint32_t nValue; | |
97 | uint32_t tValue; | |
98 | uint32_t kValue; | |
99 | uint32_t numBits = numEccBytes * 8; | |
100 | ||
101 | /* disable and reset ECC */ | |
102 | REG_UMI_BCH_CTRL_STATUS = | |
103 | REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID | | |
104 | REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID; | |
105 | ||
106 | /* Every correctible bit requires 13 ECC bits */ | |
107 | tValue = (uint32_t) (numBits / ECC_BITS_PER_CORRECTABLE_BIT); | |
108 | ||
109 | /* Total data in number of bits for generating and computing BCH ECC */ | |
110 | nValue = (NAND_DATA_ACCESS_SIZE + numEccBytes) * 8; | |
111 | ||
112 | /* K parameter is used internally. K = N - (T * 13) */ | |
113 | kValue = nValue - (tValue * ECC_BITS_PER_CORRECTABLE_BIT); | |
114 | ||
115 | /* Write the settings */ | |
116 | REG_UMI_BCH_N = nValue; | |
117 | REG_UMI_BCH_T = tValue; | |
118 | REG_UMI_BCH_K = kValue; | |
119 | } | |
120 | ||
121 | /* Pause during ECC read calculation to skip bytes in OOB */ | |
122 | static inline void nand_bcm_umi_bch_pause_read_ecc_calc(void) | |
123 | { | |
124 | REG_UMI_BCH_CTRL_STATUS = | |
125 | REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN | | |
126 | REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC; | |
127 | } | |
128 | ||
129 | /* Resume during ECC read calculation after skipping bytes in OOB */ | |
130 | static inline void nand_bcm_umi_bch_resume_read_ecc_calc(void) | |
131 | { | |
132 | REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN; | |
133 | } | |
134 | ||
135 | /* Poll read ECC calc to check when hardware completes */ | |
136 | static inline uint32_t nand_bcm_umi_bch_poll_read_ecc_calc(void) | |
137 | { | |
138 | uint32_t regVal; | |
139 | ||
140 | do { | |
141 | /* wait for ECC to be valid */ | |
142 | regVal = REG_UMI_BCH_CTRL_STATUS; | |
143 | } while ((regVal & REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID) == 0); | |
144 | ||
145 | return regVal; | |
146 | } | |
147 | ||
148 | /* Poll write ECC calc to check when hardware completes */ | |
149 | static inline void nand_bcm_umi_bch_poll_write_ecc_calc(void) | |
150 | { | |
151 | /* wait for ECC to be valid */ | |
152 | while ((REG_UMI_BCH_CTRL_STATUS & REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID) | |
153 | == 0) | |
154 | ; | |
155 | } | |
156 | ||
157 | /* Read the OOB and ECC, for kernel write OOB to a buffer */ | |
158 | #if defined(__KERNEL__) && !defined(STANDALONE) | |
159 | static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, | |
160 | uint8_t *eccCalc, int numEccBytes, uint8_t *oobp) | |
161 | #else | |
162 | static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, | |
163 | uint8_t *eccCalc, int numEccBytes) | |
164 | #endif | |
165 | { | |
166 | int eccPos = 0; | |
167 | int numToRead = 16; /* There are 16 bytes per sector in the OOB */ | |
168 | ||
169 | /* ECC is already paused when this function is called */ | |
1385858e RK |
170 | if (pageSize != NAND_DATA_ACCESS_SIZE) { |
171 | /* skip BI */ | |
172 | #if defined(__KERNEL__) && !defined(STANDALONE) | |
173 | *oobp++ = REG_NAND_DATA8; | |
174 | #else | |
175 | REG_NAND_DATA8; | |
176 | #endif | |
177 | numToRead--; | |
178 | } | |
266dead2 | 179 | |
1385858e RK |
180 | while (numToRead > numEccBytes) { |
181 | /* skip free oob region */ | |
266dead2 | 182 | #if defined(__KERNEL__) && !defined(STANDALONE) |
1385858e | 183 | *oobp++ = REG_NAND_DATA8; |
266dead2 | 184 | #else |
1385858e | 185 | REG_NAND_DATA8; |
266dead2 | 186 | #endif |
1385858e RK |
187 | numToRead--; |
188 | } | |
266dead2 | 189 | |
1385858e | 190 | if (pageSize == NAND_DATA_ACCESS_SIZE) { |
266dead2 LHC |
191 | /* read ECC bytes before BI */ |
192 | nand_bcm_umi_bch_resume_read_ecc_calc(); | |
193 | ||
194 | while (numToRead > 11) { | |
195 | #if defined(__KERNEL__) && !defined(STANDALONE) | |
196 | *oobp = REG_NAND_DATA8; | |
197 | eccCalc[eccPos++] = *oobp; | |
198 | oobp++; | |
199 | #else | |
200 | eccCalc[eccPos++] = REG_NAND_DATA8; | |
201 | #endif | |
1385858e | 202 | numToRead--; |
266dead2 LHC |
203 | } |
204 | ||
205 | nand_bcm_umi_bch_pause_read_ecc_calc(); | |
206 | ||
207 | if (numToRead == 11) { | |
208 | /* read BI */ | |
209 | #if defined(__KERNEL__) && !defined(STANDALONE) | |
210 | *oobp++ = REG_NAND_DATA8; | |
211 | #else | |
212 | REG_NAND_DATA8; | |
213 | #endif | |
214 | numToRead--; | |
215 | } | |
216 | ||
1385858e RK |
217 | } |
218 | /* read ECC bytes */ | |
219 | nand_bcm_umi_bch_resume_read_ecc_calc(); | |
220 | while (numToRead) { | |
266dead2 | 221 | #if defined(__KERNEL__) && !defined(STANDALONE) |
1385858e RK |
222 | *oobp = REG_NAND_DATA8; |
223 | eccCalc[eccPos++] = *oobp; | |
224 | oobp++; | |
266dead2 | 225 | #else |
1385858e | 226 | eccCalc[eccPos++] = REG_NAND_DATA8; |
266dead2 LHC |
227 | #endif |
228 | numToRead--; | |
266dead2 LHC |
229 | } |
230 | } | |
231 | ||
232 | /* Helper function to write ECC */ | |
233 | static inline void NAND_BCM_UMI_ECC_WRITE(int numEccBytes, int eccBytePos, | |
234 | uint8_t *oobp, uint8_t eccVal) | |
235 | { | |
236 | if (eccBytePos <= numEccBytes) | |
237 | *oobp = eccVal; | |
238 | } | |
239 | ||
240 | /* Write OOB with ECC */ | |
241 | static inline void nand_bcm_umi_bch_write_oobEcc(uint32_t pageSize, | |
242 | uint8_t *oobp, int numEccBytes) | |
243 | { | |
244 | uint32_t eccVal = 0xffffffff; | |
245 | ||
246 | /* wait for write ECC to be valid */ | |
247 | nand_bcm_umi_bch_poll_write_ecc_calc(); | |
248 | ||
249 | /* | |
250 | ** Get the hardware ecc from the 32-bit result registers. | |
251 | ** Read after 512 byte accesses. Format B3B2B1B0 | |
252 | ** where B3 = ecc3, etc. | |
253 | */ | |
254 | ||
255 | if (pageSize == NAND_DATA_ACCESS_SIZE) { | |
256 | /* Now fill in the ECC bytes */ | |
257 | if (numEccBytes >= 13) | |
258 | eccVal = REG_UMI_BCH_WR_ECC_3; | |
259 | ||
260 | /* Usually we skip CM in oob[0,1] */ | |
261 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[0], | |
262 | (eccVal >> 16) & 0xff); | |
263 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[1], | |
264 | (eccVal >> 8) & 0xff); | |
265 | ||
266 | /* Write ECC in oob[2,3,4] */ | |
267 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[2], | |
268 | eccVal & 0xff); /* ECC 12 */ | |
269 | ||
270 | if (numEccBytes >= 9) | |
271 | eccVal = REG_UMI_BCH_WR_ECC_2; | |
272 | ||
273 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[3], | |
274 | (eccVal >> 24) & 0xff); /* ECC11 */ | |
275 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[4], | |
276 | (eccVal >> 16) & 0xff); /* ECC10 */ | |
277 | ||
278 | /* Always Skip BI in oob[5] */ | |
279 | } else { | |
280 | /* Always Skip BI in oob[0] */ | |
281 | ||
282 | /* Now fill in the ECC bytes */ | |
283 | if (numEccBytes >= 13) | |
284 | eccVal = REG_UMI_BCH_WR_ECC_3; | |
285 | ||
286 | /* Usually skip CM in oob[1,2] */ | |
287 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[1], | |
288 | (eccVal >> 16) & 0xff); | |
289 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[2], | |
290 | (eccVal >> 8) & 0xff); | |
291 | ||
292 | /* Write ECC in oob[3-15] */ | |
293 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[3], | |
294 | eccVal & 0xff); /* ECC12 */ | |
295 | ||
296 | if (numEccBytes >= 9) | |
297 | eccVal = REG_UMI_BCH_WR_ECC_2; | |
298 | ||
299 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[4], | |
300 | (eccVal >> 24) & 0xff); /* ECC11 */ | |
301 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[5], | |
302 | (eccVal >> 16) & 0xff); /* ECC10 */ | |
303 | } | |
304 | ||
305 | /* Fill in the remainder of ECC locations */ | |
306 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 10, &oobp[6], | |
307 | (eccVal >> 8) & 0xff); /* ECC9 */ | |
308 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 9, &oobp[7], | |
309 | eccVal & 0xff); /* ECC8 */ | |
310 | ||
311 | if (numEccBytes >= 5) | |
312 | eccVal = REG_UMI_BCH_WR_ECC_1; | |
313 | ||
314 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 8, &oobp[8], | |
315 | (eccVal >> 24) & 0xff); /* ECC7 */ | |
316 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 7, &oobp[9], | |
317 | (eccVal >> 16) & 0xff); /* ECC6 */ | |
318 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 6, &oobp[10], | |
319 | (eccVal >> 8) & 0xff); /* ECC5 */ | |
320 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 5, &oobp[11], | |
321 | eccVal & 0xff); /* ECC4 */ | |
322 | ||
323 | if (numEccBytes >= 1) | |
324 | eccVal = REG_UMI_BCH_WR_ECC_0; | |
325 | ||
326 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 4, &oobp[12], | |
327 | (eccVal >> 24) & 0xff); /* ECC3 */ | |
328 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 3, &oobp[13], | |
329 | (eccVal >> 16) & 0xff); /* ECC2 */ | |
330 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 2, &oobp[14], | |
331 | (eccVal >> 8) & 0xff); /* ECC1 */ | |
332 | NAND_BCM_UMI_ECC_WRITE(numEccBytes, 1, &oobp[15], | |
333 | eccVal & 0xff); /* ECC0 */ | |
334 | } | |
335 | #endif | |
336 | ||
337 | #endif /* NAND_BCM_UMI_H */ |