aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/mxc_nand.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2009-06-04 11:12:26 -0400
committerSascha Hauer <s.hauer@pengutronix.de>2009-11-12 02:32:32 -0500
commitf8f9608d9b7a5b3349605284a4f3876e898413f1 (patch)
tree30f2525e4aa2a0822486d17bad356b90423d7478 /drivers/mtd/nand/mxc_nand.c
parentd970a0730ba5598b711d42bac287a17db5b6268a (diff)
mxc nand: use buffers
The NAND controller has some limitations how to access the internal buffers. It only allows 32 bit accesses. The driver used to work around this by having special alignment aware copy routines. We now copy the whole page to a buffer in memory and let the access functions use this buffer. This simplifies the driver. A bonnie++ test showed that this has no negative performance impact on the driver. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/mtd/nand/mxc_nand.c')
-rw-r--r--drivers/mtd/nand/mxc_nand.c310
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
229static void send_page(struct mxc_nand_host *host, uint8_t buf_id, 231static 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 */
456static 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
644static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) 483static 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;