1 | /* |
2 | * Copyright (C) 2009 Qi Hardware Inc., |
3 | * Author: Xiangfu Liu <xiangfu@qi-hardware.com> |
4 | * |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License |
7 | * version 3 as published by the Free Software Foundation. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | * Boston, MA 02110-1301, USA |
18 | */ |
19 | |
20 | #include "nandflash.h" |
21 | #include "jz4740.h" |
22 | #include "usb_boot.h" |
23 | #include "usb_boot_defines.h" |
24 | |
25 | #define __nand_enable() (REG_EMC_NFCSR |= EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1) |
26 | #define __nand_disable() (REG_EMC_NFCSR &= ~(EMC_NFCSR_NFCE1)) |
27 | #define __nand_ecc_rs_encoding() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST | EMC_NFECR_RS | EMC_NFECR_RS_ENCODING) |
28 | #define __nand_ecc_rs_decoding() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST | EMC_NFECR_RS | EMC_NFECR_RS_DECODING) |
29 | #define __nand_ecc_disable() (REG_EMC_NFECR &= ~EMC_NFECR_ECCE) |
30 | #define __nand_ecc_encode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_ENCF)) |
31 | #define __nand_ecc_decode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_DECF)) |
32 | |
33 | #define __nand_ready() ((REG_GPIO_PXPIN(2) & 0x40000000) ? 1 : 0) |
34 | #define __nand_ecc() (REG_EMC_NFECC & 0x00ffffff) |
35 | #define __nand_cmd(n) (REG8(cmdport) = (n)) |
36 | #define __nand_addr(n) (REG8(addrport) = (n)) |
37 | #define __nand_data8() REG8(dataport) |
38 | #define __nand_data16() REG16(dataport) |
39 | |
40 | #define CMD_READA 0x00 |
41 | #define CMD_READB 0x01 |
42 | #define CMD_READC 0x50 |
43 | #define CMD_ERASE_SETUP 0x60 |
44 | #define CMD_ERASE 0xD0 |
45 | #define CMD_READ_STATUS 0x70 |
46 | #define CMD_CONFIRM 0x30 |
47 | #define CMD_SEQIN 0x80 |
48 | #define CMD_PGPROG 0x10 |
49 | #define CMD_READID 0x90 |
50 | |
51 | #define OOB_BAD_OFF 0x00 |
52 | #define OOB_ECC_OFF 0x04 |
53 | |
54 | #define OP_ERASE 0 |
55 | #define OP_WRITE 1 |
56 | #define OP_READ 2 |
57 | |
58 | #define ECC_BLOCK 512 |
59 | #define ECC_POS 6 |
60 | #define PAR_SIZE 9 |
61 | #define ECC_SIZE 36 |
62 | |
63 | static volatile unsigned char *gpio_base = (volatile unsigned char *)0xb0010000; |
64 | static volatile unsigned char *emc_base = (volatile unsigned char *)0xb3010000; |
65 | static volatile unsigned char *addrport = (volatile unsigned char *)0xb8010000; |
66 | static volatile unsigned char *dataport = (volatile unsigned char *)0xb8000000; |
67 | static volatile unsigned char *cmdport = (volatile unsigned char *)0xb8008000; |
68 | |
69 | static int bus = 8, row = 2, pagesize = 2048, oobsize = 64, ppb = 128; |
70 | static int bad_block_pos,bad_block_page,force_erase,ecc_pos,wp_pin; |
71 | extern struct hand Hand; |
72 | static u8 oob_buf[256] = {0}; |
73 | extern u16 handshake_PKT[4]; |
74 | |
75 | #define dprintf(x) serial_puts(x) |
76 | |
77 | static unsigned int EMC_CSN[4]= |
78 | { |
79 | 0xb8000000, |
80 | 0xb4000000, |
81 | 0xa8000000, |
82 | 0xa4000000 |
83 | }; |
84 | |
85 | static inline void __nand_sync(void) |
86 | { |
87 | unsigned int timeout = 100000; |
88 | while ((REG_GPIO_PXPIN(2) & 0x40000000) && timeout--); |
89 | while (!(REG_GPIO_PXPIN(2) & 0x40000000)); |
90 | } |
91 | |
92 | static void select_chip(int block) |
93 | { |
94 | int t; |
95 | if (!Hand.nand_bpc) |
96 | return; |
97 | t = (block / Hand.nand_bpc) % 4; |
98 | addrport = (volatile unsigned char *)(EMC_CSN[t] + 0x10000); |
99 | dataport = (volatile unsigned char *)EMC_CSN[t]; |
100 | cmdport = (volatile unsigned char *)(EMC_CSN[t] + 0x8000); |
101 | } |
102 | |
103 | static int read_oob(void *buf, u32 size, u32 pg); |
104 | static int nand_data_write8(char *buf, int count); |
105 | static int nand_data_write16(char *buf, int count); |
106 | static int nand_data_read8(char *buf, int count); |
107 | static int nand_data_read16(char *buf, int count); |
108 | static int (*write_proc)(char *, int) = NULL; |
109 | static int (*read_proc)(char *, int) = NULL; |
110 | |
111 | static nand_init_gpio(void) |
112 | { |
113 | /* modify this fun to a specifical borad |
114 | * this fun init those gpio use by all flash chip |
115 | * select the gpio function related to flash chip |
116 | */ |
117 | __gpio_as_nand(); |
118 | } |
119 | |
120 | inline void nand_enable_4740(unsigned int csn) |
121 | { |
122 | /* modify this fun to a specifical borad |
123 | * this fun to enable the chip select pin csn |
124 | * the choosn chip can work after this fun |
125 | */ |
126 | __nand_enable(); |
127 | } |
128 | |
129 | inline void nand_disable_4740(unsigned int csn) |
130 | { |
131 | /* modify this fun to a specifical borad |
132 | * this fun to enable the chip select pin csn |
133 | * the choosn chip can not work after this fun |
134 | */ |
135 | __nand_disable(); |
136 | } |
137 | |
138 | unsigned int nand_query_4740(u8 *id) |
139 | { |
140 | __nand_sync(); |
141 | __nand_cmd(CMD_READID); |
142 | __nand_addr(0); |
143 | |
144 | id[0] = __nand_data8(); //VID |
145 | id[1] = __nand_data8(); //PID |
146 | id[2] = __nand_data8(); //CHIP ID |
147 | id[3] = __nand_data8(); //PAGE ID |
148 | id[4] = __nand_data8(); //PLANE ID |
149 | |
150 | return 0; |
151 | } |
152 | |
153 | int nand_init_4740(int bus_width, int row_cycle, int page_size, int page_per_block, |
154 | int bbpage,int bbpos,int force,int ep) |
155 | { |
156 | bus = bus_width; |
157 | row = row_cycle; |
158 | pagesize = page_size; |
159 | oobsize = pagesize / 32; |
160 | ppb = page_per_block; |
161 | bad_block_pos = bbpos; |
162 | bad_block_page = bbpage; |
163 | force_erase = force; |
164 | ecc_pos = ep; |
165 | wp_pin = Hand.nand_wppin; |
166 | |
167 | /* Initialize NAND Flash Pins */ |
168 | if (wp_pin) |
169 | { |
170 | __gpio_as_output(wp_pin); |
171 | __gpio_disable_pull(wp_pin); |
172 | } |
173 | nand_init_gpio(); |
174 | select_chip(0); |
175 | REG_EMC_SMCR1 = 0x0fff7700; //slow speed |
176 | // REG_EMC_SMCR1 = 0x04444400; //normal speed |
177 | // REG_EMC_SMCR1 = 0x0d221200; //fast speed |
178 | |
179 | if (bus == 8) { |
180 | write_proc = nand_data_write8; |
181 | read_proc = nand_data_read8; |
182 | } else { |
183 | write_proc = nand_data_write16; |
184 | read_proc = nand_data_read16; |
185 | } |
186 | return 0; |
187 | } |
188 | |
189 | int nand_fini_4740(void) |
190 | { |
191 | __nand_disable(); |
192 | return 0; |
193 | } |
194 | |
195 | /* |
196 | * Read oob <pagenum> pages from <startpage> page. |
197 | * Don't skip bad block. |
198 | * Don't use HW ECC. |
199 | */ |
200 | u32 nand_read_oob_4740(void *buf, u32 startpage, u32 pagenum) |
201 | { |
202 | u32 cnt, cur_page; |
203 | u8 *tmpbuf; |
204 | |
205 | tmpbuf = (u8 *)buf; |
206 | |
207 | cur_page = startpage; |
208 | cnt = 0; |
209 | while (cnt < pagenum) { |
210 | read_oob((void *)tmpbuf, oobsize, cur_page); |
211 | tmpbuf += oobsize; |
212 | cur_page++; |
213 | cnt++; |
214 | } |
215 | |
216 | return cur_page; |
217 | } |
218 | |
219 | static int nand_check_block(u32 block) |
220 | { |
221 | u32 pg,i; |
222 | |
223 | if ( bad_block_page >= ppb ) { |
224 | /* do absolute bad block detect! */ |
225 | pg = block * ppb + 0; |
226 | read_oob(oob_buf, oobsize, pg); |
227 | if ( oob_buf[0] != 0xff || oob_buf[1] != 0xff ) |
228 | goto bad; |
229 | |
230 | pg = block * ppb + 1; |
231 | read_oob(oob_buf, oobsize, pg); |
232 | if ( oob_buf[0] != 0xff || oob_buf[1] != 0xff ) |
233 | goto bad; |
234 | |
235 | pg = block * ppb + ppb - 2 ; |
236 | read_oob(oob_buf, oobsize, pg); |
237 | if ( oob_buf[0] != 0xff || oob_buf[1] != 0xff ) |
238 | goto bad; |
239 | |
240 | pg = block * ppb + ppb - 1 ; |
241 | read_oob(oob_buf, oobsize, pg); |
242 | if ( oob_buf[0] != 0xff || oob_buf[1] != 0xff ) |
243 | goto bad; |
244 | |
245 | } else { |
246 | pg = block * ppb + bad_block_page; |
247 | read_oob(oob_buf, oobsize, pg); |
248 | if (oob_buf[bad_block_pos] != 0xff) |
249 | goto bad; |
250 | |
251 | } |
252 | |
253 | return 0; |
254 | |
255 | bad: |
256 | serial_puts("Absolute skip a bad block\n"); |
257 | return 1; |
258 | |
259 | } |
260 | |
261 | /* |
262 | * Read data <pagecount> pages from <startpage> page. |
263 | * Don't skip bad block. |
264 | * Don't use HW ECC. |
265 | */ |
266 | u32 nand_read_raw_4740(void *buf, u32 startpage, u32 pagecount, int option) |
267 | { |
268 | u32 cnt, j; |
269 | u32 cur_page, rowaddr; |
270 | u8 *tmpbuf; |
271 | |
272 | tmpbuf = (u8 *)buf; |
273 | |
274 | cur_page = startpage; |
275 | cnt = 0; |
276 | while (cnt < pagecount) { |
277 | select_chip(cnt / ppb); |
278 | if ((cur_page % ppb) == 0) { |
279 | if (nand_check_block(cur_page / ppb)) { |
280 | cur_page += ppb; //Bad block, set to next block |
281 | continue; |
282 | } |
283 | } |
284 | |
285 | __nand_cmd(CMD_READA); |
286 | __nand_addr(0); |
287 | if (pagesize != 512) |
288 | __nand_addr(0); |
289 | |
290 | rowaddr = cur_page; |
291 | for (j = 0; j < row; j++) { |
292 | __nand_addr(rowaddr & 0xff); |
293 | rowaddr >>= 8; |
294 | } |
295 | |
296 | if (pagesize != 512) |
297 | __nand_cmd(CMD_CONFIRM); |
298 | |
299 | __nand_sync(); |
300 | read_proc(tmpbuf, pagesize); |
301 | |
302 | tmpbuf += pagesize; |
303 | if (option != NO_OOB) |
304 | { |
305 | read_oob(tmpbuf, oobsize, cur_page); |
306 | tmpbuf += oobsize; |
307 | } |
308 | |
309 | cur_page++; |
310 | cnt++; |
311 | } |
312 | |
313 | return cur_page; |
314 | } |
315 | |
316 | u32 nand_erase_4740(int blk_num, int sblk, int force) |
317 | { |
318 | int i, j; |
319 | u32 cur, rowaddr; |
320 | |
321 | if (wp_pin) |
322 | __gpio_set_pin(wp_pin); |
323 | cur = sblk * ppb; |
324 | for (i = 0; i < blk_num; ) { |
325 | rowaddr = cur; |
326 | select_chip(cur / ppb); |
327 | if ( !force ) { |
328 | if (nand_check_block(cur/ppb)) { |
329 | cur += ppb; |
330 | blk_num += Hand.nand_plane; |
331 | continue; |
332 | } |
333 | } |
334 | |
335 | __nand_cmd(CMD_ERASE_SETUP); |
336 | |
337 | for (j = 0; j < row; j++) { |
338 | __nand_addr(rowaddr & 0xff); |
339 | rowaddr >>= 8; |
340 | } |
341 | __nand_cmd(CMD_ERASE); |
342 | __nand_sync(); |
343 | __nand_cmd(CMD_READ_STATUS); |
344 | |
345 | if (__nand_data8() & 0x01) { |
346 | serial_puts("Erase fail at "); |
347 | serial_put_hex(cur / ppb); |
348 | nand_mark_bad_4740(cur/ppb); |
349 | cur += ppb; |
350 | blk_num += Hand.nand_plane; |
351 | continue; |
352 | } |
353 | cur += ppb; |
354 | i++; |
355 | } |
356 | |
357 | if (wp_pin) |
358 | __gpio_clear_pin(wp_pin); |
359 | return cur; |
360 | } |
361 | |
362 | static int read_oob(void *buf, u32 size, u32 pg) |
363 | { |
364 | u32 i, coladdr, rowaddr; |
365 | |
366 | select_chip(pg / ppb); |
367 | if (pagesize == 512) |
368 | coladdr = 0; |
369 | else |
370 | coladdr = pagesize; |
371 | |
372 | if (pagesize == 512) |
373 | __nand_cmd(CMD_READC); /* Send READOOB command */ |
374 | else |
375 | __nand_cmd(CMD_READA); /* Send READ0 command */ |
376 | |
377 | __nand_addr(coladdr & 0xff); /* Send column address */ |
378 | |
379 | if (pagesize != 512) |
380 | __nand_addr(coladdr >> 8); |
381 | |
382 | /* Send page address */ |
383 | rowaddr = pg; |
384 | for (i = 0; i < row; i++) { |
385 | __nand_addr(rowaddr & 0xff); |
386 | rowaddr >>= 8; |
387 | } |
388 | |
389 | /* Send READSTART command for 2048 ps NAND */ |
390 | if (pagesize != 512) |
391 | __nand_cmd(CMD_CONFIRM); |
392 | |
393 | /* Wait for device ready */ |
394 | __nand_sync(); |
395 | |
396 | /* Read oob data */ |
397 | read_proc(buf, size); |
398 | |
399 | if (pagesize == 512) |
400 | __nand_sync(); |
401 | return 0; |
402 | } |
403 | |
404 | void rs_correct(unsigned char *buf, int idx, int mask) |
405 | { |
406 | int i, j; |
407 | unsigned short d, d1, dm; |
408 | |
409 | i = (idx * 9) >> 3; |
410 | j = (idx * 9) & 0x7; |
411 | |
412 | i = (j == 0) ? (i - 1) : i; |
413 | j = (j == 0) ? 7 : (j - 1); |
414 | |
415 | d = (buf[i] << 8) | buf[i - 1]; |
416 | |
417 | d1 = (d >> j) & 0x1ff; |
418 | d1 ^= mask; |
419 | |
420 | dm = ~(0x1ff << j); |
421 | d = (d & dm) | (d1 << j); |
422 | |
423 | buf[i - 1] = d & 0xff; |
424 | buf[i] = (d >> 8) & 0xff; |
425 | } |
426 | |
427 | /* |
428 | * Read data <pagecount> pages from <startpage> page. |
429 | * Skip bad block if detected. |
430 | * HW ECC is used. |
431 | */ |
432 | u32 nand_read_4740(void *buf, u32 startpage, u32 pagecount, int option) |
433 | { |
434 | u32 j, k; |
435 | u32 cur_page, cur_blk, cnt, rowaddr, ecccnt; |
436 | u8 *tmpbuf,flag = 0; |
437 | ecccnt = pagesize / ECC_BLOCK; |
438 | cur_page = startpage; |
439 | cnt = 0; |
440 | tmpbuf = buf; |
441 | handshake_PKT[3] = 0; |
442 | |
443 | return cur_page + pagecount; |
444 | |
445 | while (cnt < pagecount) { |
446 | select_chip(cnt / ppb); |
447 | /* If this is the first page of the block, check for bad. */ |
448 | if ((cur_page % ppb) == 0) { |
449 | cur_blk = cur_page / ppb; |
450 | if (nand_check_block(cur_blk)) { |
451 | cur_page += ppb; //Bad block, set to next block |
452 | continue; |
453 | } |
454 | } |
455 | /* read oob first */ |
456 | read_oob(oob_buf, oobsize, cur_page); |
457 | __nand_cmd(CMD_READA); |
458 | |
459 | __nand_addr(0); |
460 | if (pagesize != 512) |
461 | __nand_addr(0); |
462 | |
463 | rowaddr = cur_page; |
464 | for (j = 0; j < row; j++) { |
465 | __nand_addr(rowaddr & 0xff); |
466 | rowaddr >>= 8; |
467 | } |
468 | |
469 | if (pagesize != 512) |
470 | __nand_cmd(CMD_CONFIRM); |
471 | |
472 | __nand_sync(); |
473 | |
474 | for (j = 0; j < ecccnt; j++) { |
475 | volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; |
476 | u32 stat; |
477 | flag = 0; |
478 | |
479 | REG_EMC_NFINTS = 0x0; |
480 | __nand_ecc_rs_decoding(); |
481 | read_proc(tmpbuf, ECC_BLOCK); |
482 | for (k = 0; k < PAR_SIZE; k++) { |
483 | *paraddr++ = oob_buf[ecc_pos + j*PAR_SIZE + k]; |
484 | if (oob_buf[ecc_pos + j*PAR_SIZE + k] != 0xff) |
485 | flag = 1; |
486 | } |
487 | REG_EMC_NFECR |= EMC_NFECR_PRDY; |
488 | __nand_ecc_decode_sync(); |
489 | __nand_ecc_disable(); |
490 | /* Check decoding */ |
491 | stat = REG_EMC_NFINTS; |
492 | if (stat & EMC_NFINTS_ERR) { |
493 | if (stat & EMC_NFINTS_UNCOR) { |
494 | if (flag) { |
495 | serial_puts("\nUncorrectable error occurred, Page:"); |
496 | serial_put_hex(cur_page); |
497 | handshake_PKT[3] = 1; |
498 | } |
499 | } else { |
500 | handshake_PKT[3] = 0; |
501 | u32 errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT; |
502 | switch (errcnt) { |
503 | case 4: |
504 | rs_correct(tmpbuf, (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); |
505 | case 3: |
506 | rs_correct(tmpbuf, (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); |
507 | case 2: |
508 | rs_correct(tmpbuf, (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); |
509 | case 1: |
510 | rs_correct(tmpbuf, (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT); |
511 | break; |
512 | default: |
513 | break; |
514 | } |
515 | |
516 | } |
517 | } |
518 | /* increment pointer */ |
519 | tmpbuf += ECC_BLOCK; |
520 | } |
521 | |
522 | switch (option) { |
523 | case OOB_ECC: |
524 | for (j = 0; j < oobsize; j++) |
525 | tmpbuf[j] = oob_buf[j]; |
526 | tmpbuf += oobsize; |
527 | break; |
528 | case OOB_NO_ECC: |
529 | for (j = 0; j < ecccnt * PAR_SIZE; j++) |
530 | oob_buf[ecc_pos + j] = 0xff; |
531 | for (j = 0; j < oobsize; j++) |
532 | tmpbuf[j] = oob_buf[j]; |
533 | tmpbuf += oobsize; |
534 | break; |
535 | case NO_OOB: |
536 | break; |
537 | } |
538 | |
539 | cur_page++; |
540 | cnt++; |
541 | } |
542 | return cur_page; |
543 | } |
544 | |
545 | u32 nand_program_4740(void *context, int spage, int pages, int option) |
546 | { |
547 | size_t datasize; |
548 | u32 i, j, cur, rowaddr; |
549 | u8 *tmpbuf; |
550 | u32 ecccnt,oobsize_sav,ecccnt_sav,eccpos_sav; |
551 | u8 ecc_buf[256]; |
552 | |
553 | datasize = pagesize; |
554 | if (option != NO_OOB) |
555 | datasize += oobsize; |
556 | |
557 | |
558 | if (wp_pin) |
559 | __gpio_set_pin(wp_pin); |
560 | restart: |
561 | tmpbuf = (u8 *)context; |
562 | ecccnt_sav = ecccnt = pagesize / ECC_BLOCK; |
563 | oobsize_sav = oobsize; |
564 | eccpos_sav = ecc_pos; |
565 | i = 0; |
566 | cur = spage; |
567 | |
568 | while (i < pages) { |
569 | select_chip(cur / ppb); |
570 | #if 1 |
571 | if ((pagesize == 4096) && (cur < 8)) { |
572 | ecccnt = 4; |
573 | oobsize = 64; |
574 | ecc_pos = 6; |
575 | } else { |
576 | ecccnt = ecccnt_sav; |
577 | oobsize = oobsize_sav; |
578 | ecc_pos = eccpos_sav; |
579 | } |
580 | |
581 | /* Skip 16KB after nand_spl if pagesize=4096 */ |
582 | if ((pagesize == 4096) && (cur == 8)) |
583 | tmpbuf += 16 * 1024; |
584 | #endif |
585 | |
586 | if ((cur % ppb) == 0) { |
587 | if (nand_check_block(cur / ppb)) { |
588 | cur += ppb; // Bad block, set to next block |
589 | continue; |
590 | } |
591 | } |
592 | |
593 | for (j = 0; j < datasize; ++j) { |
594 | if (tmpbuf[j] != 0xff) |
595 | break; |
596 | } |
597 | |
598 | if (j == datasize) { |
599 | tmpbuf += datasize; |
600 | ++i; |
601 | ++cur; |
602 | continue; |
603 | } |
604 | |
605 | if (pagesize == 512) |
606 | __nand_cmd(CMD_READA); |
607 | |
608 | __nand_cmd(CMD_SEQIN); |
609 | __nand_addr(0); |
610 | |
611 | if (pagesize != 512) |
612 | __nand_addr(0); |
613 | |
614 | rowaddr = cur; |
615 | for (j = 0; j < row; j++) { |
616 | __nand_addr(rowaddr & 0xff); |
617 | rowaddr >>= 8; |
618 | } |
619 | |
620 | switch (option) { |
621 | case OOB_ECC: |
622 | write_proc(tmpbuf, pagesize); //write data |
623 | tmpbuf += pagesize; |
624 | write_proc((u8 *)tmpbuf, oobsize); //write oob |
625 | tmpbuf += oobsize; |
626 | |
627 | break; |
628 | case OOB_NO_ECC: |
629 | for (j = 0; j < ecccnt; j++) { |
630 | volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; |
631 | int k; |
632 | |
633 | REG_EMC_NFINTS = 0x0; |
634 | __nand_ecc_rs_encoding(); |
635 | write_proc(tmpbuf, ECC_BLOCK); |
636 | __nand_ecc_encode_sync(); |
637 | __nand_ecc_disable(); |
638 | |
639 | /* Read PAR values */ |
640 | for (k = 0; k < PAR_SIZE; k++) { |
641 | ecc_buf[j*PAR_SIZE+k] = *paraddr++; |
642 | } |
643 | |
644 | tmpbuf += ECC_BLOCK; |
645 | } |
646 | for (j = 0; j < oobsize; j++) { |
647 | oob_buf[j] = tmpbuf[j]; |
648 | } |
649 | |
650 | for (j = 0; j < ecccnt*PAR_SIZE; j++) |
651 | oob_buf[ecc_pos + j] = ecc_buf[j]; |
652 | write_proc((u8 *)oob_buf, oobsize); |
653 | tmpbuf += oobsize; |
654 | |
655 | break; |
656 | case NO_OOB: /* bin image */ |
657 | /* write out data */ |
658 | for (j = 0; j < ecccnt; j++) { |
659 | volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0; |
660 | int k; |
661 | |
662 | REG_EMC_NFINTS = 0x0; |
663 | __nand_ecc_rs_encoding(); |
664 | write_proc(tmpbuf, ECC_BLOCK); |
665 | __nand_ecc_encode_sync(); |
666 | __nand_ecc_disable(); |
667 | |
668 | /* Read PAR values */ |
669 | for (k = 0; k < PAR_SIZE; k++) { |
670 | ecc_buf[j*PAR_SIZE+k] = *paraddr++; |
671 | } |
672 | |
673 | tmpbuf += ECC_BLOCK; |
674 | } |
675 | |
676 | for (j = 0; j < oobsize; j++) { |
677 | oob_buf[j] = 0xff; |
678 | } |
679 | |
680 | oob_buf[2] = 0; |
681 | oob_buf[3] = 0; |
682 | oob_buf[4] = 0; |
683 | |
684 | for (j = 0; j < ecccnt*PAR_SIZE; j++) { |
685 | oob_buf[ecc_pos + j] = ecc_buf[j]; |
686 | } |
687 | |
688 | write_proc((u8 *)oob_buf, oobsize); |
689 | break; |
690 | } |
691 | |
692 | /* send program confirm command */ |
693 | __nand_cmd(CMD_PGPROG); |
694 | __nand_sync(); |
695 | |
696 | __nand_cmd(CMD_READ_STATUS); |
697 | |
698 | /* page program error */ |
699 | if (__nand_data8() & 0x01) { |
700 | serial_puts("Skip a write fail block\n"); |
701 | nand_erase_4740(1, cur/ppb, 1); //force erase before |
702 | nand_mark_bad_4740(cur / ppb); |
703 | spage += ppb; |
704 | goto restart; |
705 | } |
706 | |
707 | i ++; |
708 | cur ++; |
709 | } |
710 | |
711 | if (wp_pin) |
712 | __gpio_clear_pin(wp_pin); |
713 | |
714 | ecccnt = ecccnt_sav; |
715 | oobsize = oobsize_sav; |
716 | ecc_pos = eccpos_sav; |
717 | |
718 | return cur; |
719 | } |
720 | |
721 | static u32 nand_mark_bad_page(u32 page) |
722 | { |
723 | u8 badbuf[4096 + 128]; |
724 | u32 i; |
725 | |
726 | if (wp_pin) |
727 | __gpio_set_pin(wp_pin); |
728 | //all set to 0x00 |
729 | for (i = 0; i < pagesize + oobsize; i++) |
730 | badbuf[i] = 0x00; |
731 | |
732 | __nand_cmd(CMD_READA); |
733 | __nand_cmd(CMD_SEQIN); |
734 | |
735 | __nand_addr(0); |
736 | if (pagesize != 512) |
737 | __nand_addr(0); |
738 | for (i = 0; i < row; i++) { |
739 | __nand_addr(page & 0xff); |
740 | page >>= 8; |
741 | } |
742 | |
743 | write_proc((char *)badbuf, pagesize + oobsize); |
744 | __nand_cmd(CMD_PGPROG); |
745 | __nand_sync(); |
746 | |
747 | if (wp_pin) |
748 | __gpio_clear_pin(wp_pin); |
749 | return page; |
750 | } |
751 | |
752 | |
753 | u32 nand_mark_bad_4740(int block) |
754 | { |
755 | u32 rowaddr; |
756 | |
757 | //nand_erase_4740( 1, block, 1); //force erase before |
758 | if ( bad_block_page >= ppb ) { //mark four page! |
759 | rowaddr = block * ppb + 0; |
760 | nand_mark_bad_page(rowaddr); |
761 | |
762 | rowaddr = block * ppb + 1; |
763 | nand_mark_bad_page(rowaddr); |
764 | |
765 | rowaddr = block * ppb + ppb - 2; |
766 | nand_mark_bad_page(rowaddr); |
767 | |
768 | rowaddr = block * ppb + ppb - 1; |
769 | nand_mark_bad_page(rowaddr); |
770 | } else { //mark one page only |
771 | rowaddr = block * ppb + bad_block_page; |
772 | nand_mark_bad_page(rowaddr); |
773 | } |
774 | |
775 | return rowaddr; |
776 | } |
777 | |
778 | static int nand_data_write8(char *buf, int count) |
779 | { |
780 | int i; |
781 | u8 *p = (u8 *)buf; |
782 | for (i=0;i<count;i++) |
783 | __nand_data8() = *p++; |
784 | return 0; |
785 | } |
786 | |
787 | static int nand_data_write16(char *buf, int count) |
788 | { |
789 | int i; |
790 | u16 *p = (u16 *)buf; |
791 | for (i=0;i<count/2;i++) |
792 | __nand_data16() = *p++; |
793 | return 0; |
794 | } |
795 | |
796 | static int nand_data_read8(char *buf, int count) |
797 | { |
798 | int i; |
799 | u8 *p = (u8 *)buf; |
800 | for (i=0;i<count;i++) |
801 | *p++ = __nand_data8(); |
802 | return 0; |
803 | } |
804 | |
805 | static int nand_data_read16(char *buf, int count) |
806 | { |
807 | int i; |
808 | u16 *p = (u16 *)buf; |
809 | for (i=0; i < count / 2; i++) |
810 | *p++ = __nand_data16(); |
811 | return 0; |
812 | } |
813 | |