diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 310 |
1 files changed, 77 insertions, 233 deletions
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 8fcb33da8efc..03d20086170b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c | |||
@@ -107,15 +107,17 @@ struct mxc_nand_host { | |||
107 | struct device *dev; | 107 | struct device *dev; |
108 | 108 | ||
109 | void __iomem *regs; | 109 | void __iomem *regs; |
110 | int spare_only; | ||
111 | int status_request; | 110 | int status_request; |
112 | int pagesize_2k; | 111 | int pagesize_2k; |
113 | uint16_t col_addr; | ||
114 | struct clk *clk; | 112 | struct clk *clk; |
115 | int clk_act; | 113 | int clk_act; |
116 | int irq; | 114 | int irq; |
117 | 115 | ||
118 | wait_queue_head_t irq_waitq; | 116 | wait_queue_head_t irq_waitq; |
117 | |||
118 | uint8_t *data_buf; | ||
119 | unsigned int buf_start; | ||
120 | int spare_len; | ||
119 | }; | 121 | }; |
120 | 122 | ||
121 | /* Define delays in microsec for NAND device operations */ | 123 | /* Define delays in microsec for NAND device operations */ |
@@ -227,23 +229,11 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) | |||
227 | } | 229 | } |
228 | 230 | ||
229 | static void send_page(struct mxc_nand_host *host, uint8_t buf_id, | 231 | static void send_page(struct mxc_nand_host *host, uint8_t buf_id, |
230 | int spare_only, unsigned int ops) | 232 | unsigned int ops) |
231 | { | 233 | { |
232 | DEBUG(MTD_DEBUG_LEVEL3, "send_page (%d)\n", spare_only); | ||
233 | |||
234 | /* NANDFC buffer 0 is used for page read/write */ | 234 | /* NANDFC buffer 0 is used for page read/write */ |
235 | writew(buf_id, host->regs + NFC_BUF_ADDR); | 235 | writew(buf_id, host->regs + NFC_BUF_ADDR); |
236 | 236 | ||
237 | /* Configure spare or page+spare access */ | ||
238 | if (!host->pagesize_2k) { | ||
239 | uint16_t config1 = readw(host->regs + NFC_CONFIG1); | ||
240 | if (spare_only) | ||
241 | config1 |= NFC_SP_EN; | ||
242 | else | ||
243 | config1 &= ~(NFC_SP_EN); | ||
244 | writew(config1, host->regs + NFC_CONFIG1); | ||
245 | } | ||
246 | |||
247 | writew(ops, host->regs + NFC_CONFIG2); | 237 | writew(ops, host->regs + NFC_CONFIG2); |
248 | 238 | ||
249 | /* Wait for operation to complete */ | 239 | /* Wait for operation to complete */ |
@@ -278,6 +268,7 @@ static void send_read_id(struct mxc_nand_host *host) | |||
278 | writeb(readb(main_buf + 8), main_buf + 4); | 268 | writeb(readb(main_buf + 8), main_buf + 4); |
279 | writeb(readb(main_buf + 10), main_buf + 5); | 269 | writeb(readb(main_buf + 10), main_buf + 5); |
280 | } | 270 | } |
271 | memcpy(host->data_buf, host->regs + MAIN_AREA0, 16); | ||
281 | } | 272 | } |
282 | 273 | ||
283 | /* This function requests the NANDFC to perform a read of the | 274 | /* This function requests the NANDFC to perform a read of the |
@@ -363,32 +354,14 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd) | |||
363 | { | 354 | { |
364 | struct nand_chip *nand_chip = mtd->priv; | 355 | struct nand_chip *nand_chip = mtd->priv; |
365 | struct mxc_nand_host *host = nand_chip->priv; | 356 | struct mxc_nand_host *host = nand_chip->priv; |
366 | uint8_t ret = 0; | 357 | uint8_t ret; |
367 | uint16_t col, rd_word; | ||
368 | uint16_t __iomem *main_buf = host->regs + MAIN_AREA0; | ||
369 | uint16_t __iomem *spare_buf = host->regs + SPARE_AREA0; | ||
370 | 358 | ||
371 | /* Check for status request */ | 359 | /* Check for status request */ |
372 | if (host->status_request) | 360 | if (host->status_request) |
373 | return get_dev_status(host) & 0xFF; | 361 | return get_dev_status(host) & 0xFF; |
374 | 362 | ||
375 | /* Get column for 16-bit access */ | 363 | ret = *(uint8_t *)(host->data_buf + host->buf_start); |
376 | col = host->col_addr >> 1; | 364 | host->buf_start++; |
377 | |||
378 | /* If we are accessing the spare region */ | ||
379 | if (host->spare_only) | ||
380 | rd_word = readw(&spare_buf[col]); | ||
381 | else | ||
382 | rd_word = readw(&main_buf[col]); | ||
383 | |||
384 | /* Pick upper/lower byte of word from RAM buffer */ | ||
385 | if (host->col_addr & 0x1) | ||
386 | ret = (rd_word >> 8) & 0xFF; | ||
387 | else | ||
388 | ret = rd_word & 0xFF; | ||
389 | |||
390 | /* Update saved column address */ | ||
391 | host->col_addr++; | ||
392 | 365 | ||
393 | return ret; | 366 | return ret; |
394 | } | 367 | } |
@@ -397,33 +370,10 @@ static uint16_t mxc_nand_read_word(struct mtd_info *mtd) | |||
397 | { | 370 | { |
398 | struct nand_chip *nand_chip = mtd->priv; | 371 | struct nand_chip *nand_chip = mtd->priv; |
399 | struct mxc_nand_host *host = nand_chip->priv; | 372 | struct mxc_nand_host *host = nand_chip->priv; |
400 | uint16_t col, rd_word, ret; | 373 | uint16_t ret; |
401 | uint16_t __iomem *p; | ||
402 | 374 | ||
403 | DEBUG(MTD_DEBUG_LEVEL3, | 375 | ret = *(uint16_t *)(host->data_buf + host->buf_start); |
404 | "mxc_nand_read_word(col = %d)\n", host->col_addr); | 376 | host->buf_start += 2; |
405 | |||
406 | col = host->col_addr; | ||
407 | /* Adjust saved column address */ | ||
408 | if (col < mtd->writesize && host->spare_only) | ||
409 | col += mtd->writesize; | ||
410 | |||
411 | if (col < mtd->writesize) | ||
412 | p = (host->regs + MAIN_AREA0) + (col >> 1); | ||
413 | else | ||
414 | p = (host->regs + SPARE_AREA0) + ((col - mtd->writesize) >> 1); | ||
415 | |||
416 | if (col & 1) { | ||
417 | rd_word = readw(p); | ||
418 | ret = (rd_word >> 8) & 0xff; | ||
419 | rd_word = readw(&p[1]); | ||
420 | ret |= (rd_word << 8) & 0xff00; | ||
421 | |||
422 | } else | ||
423 | ret = readw(p); | ||
424 | |||
425 | /* Update saved column address */ | ||
426 | host->col_addr = col + 2; | ||
427 | 377 | ||
428 | return ret; | 378 | return ret; |
429 | } | 379 | } |
@@ -436,94 +386,14 @@ static void mxc_nand_write_buf(struct mtd_info *mtd, | |||
436 | { | 386 | { |
437 | struct nand_chip *nand_chip = mtd->priv; | 387 | struct nand_chip *nand_chip = mtd->priv; |
438 | struct mxc_nand_host *host = nand_chip->priv; | 388 | struct mxc_nand_host *host = nand_chip->priv; |
439 | int n, col, i = 0; | 389 | u16 col = host->buf_start; |
440 | 390 | int n = mtd->oobsize + mtd->writesize - col; | |
441 | DEBUG(MTD_DEBUG_LEVEL3, | ||
442 | "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, | ||
443 | len); | ||
444 | |||
445 | col = host->col_addr; | ||
446 | |||
447 | /* Adjust saved column address */ | ||
448 | if (col < mtd->writesize && host->spare_only) | ||
449 | col += mtd->writesize; | ||
450 | |||
451 | n = mtd->writesize + mtd->oobsize - col; | ||
452 | n = min(len, n); | ||
453 | |||
454 | DEBUG(MTD_DEBUG_LEVEL3, | ||
455 | "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); | ||
456 | |||
457 | while (n) { | ||
458 | void __iomem *p; | ||
459 | |||
460 | if (col < mtd->writesize) | ||
461 | p = host->regs + MAIN_AREA0 + (col & ~3); | ||
462 | else | ||
463 | p = host->regs + SPARE_AREA0 - | ||
464 | mtd->writesize + (col & ~3); | ||
465 | |||
466 | DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__, | ||
467 | __LINE__, p); | ||
468 | |||
469 | if (((col | (int)&buf[i]) & 3) || n < 16) { | ||
470 | uint32_t data = 0; | ||
471 | |||
472 | if (col & 3 || n < 4) | ||
473 | data = readl(p); | ||
474 | |||
475 | switch (col & 3) { | ||
476 | case 0: | ||
477 | if (n) { | ||
478 | data = (data & 0xffffff00) | | ||
479 | (buf[i++] << 0); | ||
480 | n--; | ||
481 | col++; | ||
482 | } | ||
483 | case 1: | ||
484 | if (n) { | ||
485 | data = (data & 0xffff00ff) | | ||
486 | (buf[i++] << 8); | ||
487 | n--; | ||
488 | col++; | ||
489 | } | ||
490 | case 2: | ||
491 | if (n) { | ||
492 | data = (data & 0xff00ffff) | | ||
493 | (buf[i++] << 16); | ||
494 | n--; | ||
495 | col++; | ||
496 | } | ||
497 | case 3: | ||
498 | if (n) { | ||
499 | data = (data & 0x00ffffff) | | ||
500 | (buf[i++] << 24); | ||
501 | n--; | ||
502 | col++; | ||
503 | } | ||
504 | } | ||
505 | |||
506 | writel(data, p); | ||
507 | } else { | ||
508 | int m = mtd->writesize - col; | ||
509 | 391 | ||
510 | if (col >= mtd->writesize) | 392 | n = min(n, len); |
511 | m += mtd->oobsize; | ||
512 | 393 | ||
513 | m = min(n, m) & ~3; | 394 | memcpy(host->data_buf + col, buf, n); |
514 | 395 | ||
515 | DEBUG(MTD_DEBUG_LEVEL3, | 396 | host->buf_start += n; |
516 | "%s:%d: n = %d, m = %d, i = %d, col = %d\n", | ||
517 | __func__, __LINE__, n, m, i, col); | ||
518 | |||
519 | memcpy(p, &buf[i], m); | ||
520 | col += m; | ||
521 | i += m; | ||
522 | n -= m; | ||
523 | } | ||
524 | } | ||
525 | /* Update saved column address */ | ||
526 | host->col_addr = col; | ||
527 | } | 397 | } |
528 | 398 | ||
529 | /* Read the data buffer from the NAND Flash. To read the data from NAND | 399 | /* Read the data buffer from the NAND Flash. To read the data from NAND |
@@ -534,75 +404,14 @@ static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) | |||
534 | { | 404 | { |
535 | struct nand_chip *nand_chip = mtd->priv; | 405 | struct nand_chip *nand_chip = mtd->priv; |
536 | struct mxc_nand_host *host = nand_chip->priv; | 406 | struct mxc_nand_host *host = nand_chip->priv; |
537 | int n, col, i = 0; | 407 | u16 col = host->buf_start; |
408 | int n = mtd->oobsize + mtd->writesize - col; | ||
538 | 409 | ||
539 | DEBUG(MTD_DEBUG_LEVEL3, | 410 | n = min(n, len); |
540 | "mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, len); | ||
541 | |||
542 | col = host->col_addr; | ||
543 | |||
544 | /* Adjust saved column address */ | ||
545 | if (col < mtd->writesize && host->spare_only) | ||
546 | col += mtd->writesize; | ||
547 | |||
548 | n = mtd->writesize + mtd->oobsize - col; | ||
549 | n = min(len, n); | ||
550 | |||
551 | while (n) { | ||
552 | void __iomem *p; | ||
553 | |||
554 | if (col < mtd->writesize) | ||
555 | p = host->regs + MAIN_AREA0 + (col & ~3); | ||
556 | else | ||
557 | p = host->regs + SPARE_AREA0 - | ||
558 | mtd->writesize + (col & ~3); | ||
559 | |||
560 | if (((col | (int)&buf[i]) & 3) || n < 16) { | ||
561 | uint32_t data; | ||
562 | |||
563 | data = readl(p); | ||
564 | switch (col & 3) { | ||
565 | case 0: | ||
566 | if (n) { | ||
567 | buf[i++] = (uint8_t) (data); | ||
568 | n--; | ||
569 | col++; | ||
570 | } | ||
571 | case 1: | ||
572 | if (n) { | ||
573 | buf[i++] = (uint8_t) (data >> 8); | ||
574 | n--; | ||
575 | col++; | ||
576 | } | ||
577 | case 2: | ||
578 | if (n) { | ||
579 | buf[i++] = (uint8_t) (data >> 16); | ||
580 | n--; | ||
581 | col++; | ||
582 | } | ||
583 | case 3: | ||
584 | if (n) { | ||
585 | buf[i++] = (uint8_t) (data >> 24); | ||
586 | n--; | ||
587 | col++; | ||
588 | } | ||
589 | } | ||
590 | } else { | ||
591 | int m = mtd->writesize - col; | ||
592 | 411 | ||
593 | if (col >= mtd->writesize) | 412 | memcpy(buf, host->data_buf + col, len); |
594 | m += mtd->oobsize; | ||
595 | |||
596 | m = min(n, m) & ~3; | ||
597 | memcpy(&buf[i], p, m); | ||
598 | col += m; | ||
599 | i += m; | ||
600 | n -= m; | ||
601 | } | ||
602 | } | ||
603 | /* Update saved column address */ | ||
604 | host->col_addr = col; | ||
605 | 413 | ||
414 | host->buf_start += len; | ||
606 | } | 415 | } |
607 | 416 | ||
608 | /* Used by the upper layer to verify the data in NAND Flash | 417 | /* Used by the upper layer to verify the data in NAND Flash |
@@ -641,6 +450,36 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) | |||
641 | } | 450 | } |
642 | } | 451 | } |
643 | 452 | ||
453 | /* | ||
454 | * Function to transfer data to/from spare area. | ||
455 | */ | ||
456 | static void copy_spare(struct mtd_info *mtd, bool bfrom) | ||
457 | { | ||
458 | struct nand_chip *this = mtd->priv; | ||
459 | struct mxc_nand_host *host = this->priv; | ||
460 | u16 i, j; | ||
461 | u16 n = mtd->writesize >> 9; | ||
462 | u8 *d = host->data_buf + mtd->writesize; | ||
463 | u8 *s = host->regs + SPARE_AREA0; | ||
464 | u16 t = host->spare_len; | ||
465 | |||
466 | j = (mtd->oobsize / n >> 1) << 1; | ||
467 | |||
468 | if (bfrom) { | ||
469 | for (i = 0; i < n - 1; i++) | ||
470 | memcpy(d + i * j, s + i * t, j); | ||
471 | |||
472 | /* the last section */ | ||
473 | memcpy(d + i * j, s + i * t, mtd->oobsize - i * j); | ||
474 | } else { | ||
475 | for (i = 0; i < n - 1; i++) | ||
476 | memcpy(&s[i * t], &d[i * j], j); | ||
477 | |||
478 | /* the last section */ | ||
479 | memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j); | ||
480 | } | ||
481 | } | ||
482 | |||
644 | static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) | 483 | static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) |
645 | { | 484 | { |
646 | struct nand_chip *nand_chip = mtd->priv; | 485 | struct nand_chip *nand_chip = mtd->priv; |
@@ -707,19 +546,18 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, | |||
707 | switch (command) { | 546 | switch (command) { |
708 | 547 | ||
709 | case NAND_CMD_STATUS: | 548 | case NAND_CMD_STATUS: |
710 | host->col_addr = 0; | 549 | host->buf_start = 0; |
711 | host->status_request = true; | 550 | host->status_request = true; |
712 | break; | 551 | break; |
713 | 552 | ||
714 | case NAND_CMD_READ0: | 553 | case NAND_CMD_READ0: |
715 | host->col_addr = column; | 554 | host->buf_start = column; |
716 | host->spare_only = false; | ||
717 | useirq = false; | 555 | useirq = false; |
718 | break; | 556 | break; |
719 | 557 | ||
720 | case NAND_CMD_READOOB: | 558 | case NAND_CMD_READOOB: |
721 | host->col_addr = column; | 559 | host->buf_start = column + mtd->writesize; |
722 | host->spare_only = true; | 560 | |
723 | useirq = false; | 561 | useirq = false; |
724 | if (host->pagesize_2k) | 562 | if (host->pagesize_2k) |
725 | command = NAND_CMD_READ0; /* only READ0 is valid */ | 563 | command = NAND_CMD_READ0; /* only READ0 is valid */ |
@@ -739,15 +577,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, | |||
739 | mxc_nand_command(mtd, NAND_CMD_READ0, 0, | 577 | mxc_nand_command(mtd, NAND_CMD_READ0, 0, |
740 | page_addr); | 578 | page_addr); |
741 | 579 | ||
742 | host->col_addr = column - mtd->writesize; | 580 | host->buf_start = column; |
743 | host->spare_only = true; | ||
744 | 581 | ||
745 | /* Set program pointer to spare region */ | 582 | /* Set program pointer to spare region */ |
746 | if (!host->pagesize_2k) | 583 | if (!host->pagesize_2k) |
747 | send_cmd(host, NAND_CMD_READOOB, false); | 584 | send_cmd(host, NAND_CMD_READOOB, false); |
748 | } else { | 585 | } else { |
749 | host->spare_only = false; | 586 | host->buf_start = column; |
750 | host->col_addr = column; | ||
751 | 587 | ||
752 | /* Set program pointer to page start */ | 588 | /* Set program pointer to page start */ |
753 | if (!host->pagesize_2k) | 589 | if (!host->pagesize_2k) |
@@ -757,13 +593,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, | |||
757 | break; | 593 | break; |
758 | 594 | ||
759 | case NAND_CMD_PAGEPROG: | 595 | case NAND_CMD_PAGEPROG: |
760 | send_page(host, 0, host->spare_only, NFC_INPUT); | 596 | memcpy(host->regs + MAIN_AREA0, host->data_buf, mtd->writesize); |
597 | copy_spare(mtd, false); | ||
598 | send_page(host, 0, NFC_INPUT); | ||
761 | 599 | ||
762 | if (host->pagesize_2k) { | 600 | if (host->pagesize_2k) { |
763 | /* data in 4 areas datas */ | 601 | /* data in 4 areas datas */ |
764 | send_page(host, 1, host->spare_only, NFC_INPUT); | 602 | send_page(host, 1, NFC_INPUT); |
765 | send_page(host, 2, host->spare_only, NFC_INPUT); | 603 | send_page(host, 2, NFC_INPUT); |
766 | send_page(host, 3, host->spare_only, NFC_INPUT); | 604 | send_page(host, 3, NFC_INPUT); |
767 | } | 605 | } |
768 | 606 | ||
769 | break; | 607 | break; |
@@ -789,16 +627,18 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, | |||
789 | /* send read confirm command */ | 627 | /* send read confirm command */ |
790 | send_cmd(host, NAND_CMD_READSTART, true); | 628 | send_cmd(host, NAND_CMD_READSTART, true); |
791 | /* read for each AREA */ | 629 | /* read for each AREA */ |
792 | send_page(host, 0, host->spare_only, NFC_OUTPUT); | 630 | send_page(host, 0, NFC_OUTPUT); |
793 | send_page(host, 1, host->spare_only, NFC_OUTPUT); | 631 | send_page(host, 1, NFC_OUTPUT); |
794 | send_page(host, 2, host->spare_only, NFC_OUTPUT); | 632 | send_page(host, 2, NFC_OUTPUT); |
795 | send_page(host, 3, host->spare_only, NFC_OUTPUT); | 633 | send_page(host, 3, NFC_OUTPUT); |
796 | } else | 634 | } else |
797 | send_page(host, 0, host->spare_only, NFC_OUTPUT); | 635 | send_page(host, 0, NFC_OUTPUT); |
636 | |||
637 | memcpy(host->data_buf, host->regs + MAIN_AREA0, mtd->writesize); | ||
638 | copy_spare(mtd, true); | ||
798 | break; | 639 | break; |
799 | 640 | ||
800 | case NAND_CMD_READID: | 641 | case NAND_CMD_READID: |
801 | host->col_addr = 0; | ||
802 | send_read_id(host); | 642 | send_read_id(host); |
803 | break; | 643 | break; |
804 | 644 | ||
@@ -824,10 +664,14 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
824 | int err = 0, nr_parts = 0; | 664 | int err = 0, nr_parts = 0; |
825 | 665 | ||
826 | /* Allocate memory for MTD device structure and private data */ | 666 | /* Allocate memory for MTD device structure and private data */ |
827 | host = kzalloc(sizeof(struct mxc_nand_host), GFP_KERNEL); | 667 | host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + |
668 | NAND_MAX_OOBSIZE, GFP_KERNEL); | ||
828 | if (!host) | 669 | if (!host) |
829 | return -ENOMEM; | 670 | return -ENOMEM; |
830 | 671 | ||
672 | host->data_buf = (uint8_t *)(host + 1); | ||
673 | host->spare_len = 16; | ||
674 | |||
831 | host->dev = &pdev->dev; | 675 | host->dev = &pdev->dev; |
832 | /* structures must be linked */ | 676 | /* structures must be linked */ |
833 | this = &host->nand; | 677 | this = &host->nand; |