diff options
Diffstat (limited to 'drivers/mtd/onenand/onenand_base.c')
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 596 |
1 files changed, 437 insertions, 159 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 2da6bb26353e..7f1cb6e5dccb 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * linux/drivers/mtd/onenand/onenand_base.c | 2 | * linux/drivers/mtd/onenand/onenand_base.c |
3 | * | 3 | * |
4 | * Copyright (C) 2005-2006 Samsung Electronics | 4 | * Copyright (C) 2005-2007 Samsung Electronics |
5 | * Kyungmin Park <kyungmin.park@samsung.com> | 5 | * Kyungmin Park <kyungmin.park@samsung.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
@@ -94,16 +94,9 @@ static void onenand_writew(unsigned short value, void __iomem *addr) | |||
94 | */ | 94 | */ |
95 | static int onenand_block_address(struct onenand_chip *this, int block) | 95 | static int onenand_block_address(struct onenand_chip *this, int block) |
96 | { | 96 | { |
97 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | 97 | /* Device Flash Core select, NAND Flash Block Address */ |
98 | /* Device Flash Core select, NAND Flash Block Address */ | 98 | if (block & this->density_mask) |
99 | int dfs = 0; | 99 | return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); |
100 | |||
101 | if (block & this->density_mask) | ||
102 | dfs = 1; | ||
103 | |||
104 | return (dfs << ONENAND_DDP_SHIFT) | | ||
105 | (block & (this->density_mask - 1)); | ||
106 | } | ||
107 | 100 | ||
108 | return block; | 101 | return block; |
109 | } | 102 | } |
@@ -118,17 +111,11 @@ static int onenand_block_address(struct onenand_chip *this, int block) | |||
118 | */ | 111 | */ |
119 | static int onenand_bufferram_address(struct onenand_chip *this, int block) | 112 | static int onenand_bufferram_address(struct onenand_chip *this, int block) |
120 | { | 113 | { |
121 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | 114 | /* Device BufferRAM Select */ |
122 | /* Device BufferRAM Select */ | 115 | if (block & this->density_mask) |
123 | int dbs = 0; | 116 | return ONENAND_DDP_CHIP1; |
124 | |||
125 | if (block & this->density_mask) | ||
126 | dbs = 1; | ||
127 | 117 | ||
128 | return (dbs << ONENAND_DDP_SHIFT); | 118 | return ONENAND_DDP_CHIP0; |
129 | } | ||
130 | |||
131 | return 0; | ||
132 | } | 119 | } |
133 | 120 | ||
134 | /** | 121 | /** |
@@ -317,22 +304,25 @@ static int onenand_wait(struct mtd_info *mtd, int state) | |||
317 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | 304 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); |
318 | 305 | ||
319 | if (ctrl & ONENAND_CTRL_ERROR) { | 306 | if (ctrl & ONENAND_CTRL_ERROR) { |
320 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); | 307 | printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); |
321 | if (ctrl & ONENAND_CTRL_LOCK) | 308 | if (ctrl & ONENAND_CTRL_LOCK) |
322 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n"); | 309 | printk(KERN_ERR "onenand_wait: it's locked error.\n"); |
323 | return ctrl; | 310 | return ctrl; |
324 | } | 311 | } |
325 | 312 | ||
326 | if (interrupt & ONENAND_INT_READ) { | 313 | if (interrupt & ONENAND_INT_READ) { |
327 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 314 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); |
328 | if (ecc) { | 315 | if (ecc) { |
329 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); | 316 | printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); |
330 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 317 | if (ecc & ONENAND_ECC_2BIT_ALL) { |
331 | mtd->ecc_stats.failed++; | 318 | mtd->ecc_stats.failed++; |
332 | return ecc; | 319 | return ecc; |
333 | } else if (ecc & ONENAND_ECC_1BIT_ALL) | 320 | } else if (ecc & ONENAND_ECC_1BIT_ALL) |
334 | mtd->ecc_stats.corrected++; | 321 | mtd->ecc_stats.corrected++; |
335 | } | 322 | } |
323 | } else if (state == FL_READING) { | ||
324 | printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); | ||
325 | return -EIO; | ||
336 | } | 326 | } |
337 | 327 | ||
338 | return 0; | 328 | return 0; |
@@ -587,22 +577,32 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, | |||
587 | static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | 577 | static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) |
588 | { | 578 | { |
589 | struct onenand_chip *this = mtd->priv; | 579 | struct onenand_chip *this = mtd->priv; |
590 | int block, page; | 580 | int blockpage, found = 0; |
591 | int i; | 581 | unsigned int i; |
592 | 582 | ||
593 | block = (int) (addr >> this->erase_shift); | 583 | blockpage = (int) (addr >> this->page_shift); |
594 | page = (int) (addr >> this->page_shift); | ||
595 | page &= this->page_mask; | ||
596 | 584 | ||
585 | /* Is there valid data? */ | ||
597 | i = ONENAND_CURRENT_BUFFERRAM(this); | 586 | i = ONENAND_CURRENT_BUFFERRAM(this); |
587 | if (this->bufferram[i].blockpage == blockpage) | ||
588 | found = 1; | ||
589 | else { | ||
590 | /* Check another BufferRAM */ | ||
591 | i = ONENAND_NEXT_BUFFERRAM(this); | ||
592 | if (this->bufferram[i].blockpage == blockpage) { | ||
593 | ONENAND_SET_NEXT_BUFFERRAM(this); | ||
594 | found = 1; | ||
595 | } | ||
596 | } | ||
598 | 597 | ||
599 | /* Is there valid data? */ | 598 | if (found && ONENAND_IS_DDP(this)) { |
600 | if (this->bufferram[i].block == block && | 599 | /* Select DataRAM for DDP */ |
601 | this->bufferram[i].page == page && | 600 | int block = (int) (addr >> this->erase_shift); |
602 | this->bufferram[i].valid) | 601 | int value = onenand_bufferram_address(this, block); |
603 | return 1; | 602 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); |
603 | } | ||
604 | 604 | ||
605 | return 0; | 605 | return found; |
606 | } | 606 | } |
607 | 607 | ||
608 | /** | 608 | /** |
@@ -613,31 +613,49 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | |||
613 | * | 613 | * |
614 | * Update BufferRAM information | 614 | * Update BufferRAM information |
615 | */ | 615 | */ |
616 | static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, | 616 | static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, |
617 | int valid) | 617 | int valid) |
618 | { | 618 | { |
619 | struct onenand_chip *this = mtd->priv; | 619 | struct onenand_chip *this = mtd->priv; |
620 | int block, page; | 620 | int blockpage; |
621 | int i; | 621 | unsigned int i; |
622 | 622 | ||
623 | block = (int) (addr >> this->erase_shift); | 623 | blockpage = (int) (addr >> this->page_shift); |
624 | page = (int) (addr >> this->page_shift); | ||
625 | page &= this->page_mask; | ||
626 | 624 | ||
627 | /* Invalidate BufferRAM */ | 625 | /* Invalidate another BufferRAM */ |
628 | for (i = 0; i < MAX_BUFFERRAM; i++) { | 626 | i = ONENAND_NEXT_BUFFERRAM(this); |
629 | if (this->bufferram[i].block == block && | 627 | if (this->bufferram[i].blockpage == blockpage) |
630 | this->bufferram[i].page == page) | 628 | this->bufferram[i].blockpage = -1; |
631 | this->bufferram[i].valid = 0; | ||
632 | } | ||
633 | 629 | ||
634 | /* Update BufferRAM */ | 630 | /* Update BufferRAM */ |
635 | i = ONENAND_CURRENT_BUFFERRAM(this); | 631 | i = ONENAND_CURRENT_BUFFERRAM(this); |
636 | this->bufferram[i].block = block; | 632 | if (valid) |
637 | this->bufferram[i].page = page; | 633 | this->bufferram[i].blockpage = blockpage; |
638 | this->bufferram[i].valid = valid; | 634 | else |
635 | this->bufferram[i].blockpage = -1; | ||
636 | } | ||
639 | 637 | ||
640 | return 0; | 638 | /** |
639 | * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information | ||
640 | * @param mtd MTD data structure | ||
641 | * @param addr start address to invalidate | ||
642 | * @param len length to invalidate | ||
643 | * | ||
644 | * Invalidate BufferRAM information | ||
645 | */ | ||
646 | static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, | ||
647 | unsigned int len) | ||
648 | { | ||
649 | struct onenand_chip *this = mtd->priv; | ||
650 | int i; | ||
651 | loff_t end_addr = addr + len; | ||
652 | |||
653 | /* Invalidate BufferRAM */ | ||
654 | for (i = 0; i < MAX_BUFFERRAM; i++) { | ||
655 | loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; | ||
656 | if (buf_addr >= addr && buf_addr < end_addr) | ||
657 | this->bufferram[i].blockpage = -1; | ||
658 | } | ||
641 | } | 659 | } |
642 | 660 | ||
643 | /** | 661 | /** |
@@ -716,7 +734,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
716 | 734 | ||
717 | /* Do not allow reads past end of device */ | 735 | /* Do not allow reads past end of device */ |
718 | if ((from + len) > mtd->size) { | 736 | if ((from + len) > mtd->size) { |
719 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n"); | 737 | printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n"); |
720 | *retlen = 0; | 738 | *retlen = 0; |
721 | return -EINVAL; | 739 | return -EINVAL; |
722 | } | 740 | } |
@@ -724,8 +742,6 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
724 | /* Grab the lock and see if the device is available */ | 742 | /* Grab the lock and see if the device is available */ |
725 | onenand_get_device(mtd, FL_READING); | 743 | onenand_get_device(mtd, FL_READING); |
726 | 744 | ||
727 | /* TODO handling oob */ | ||
728 | |||
729 | stats = mtd->ecc_stats; | 745 | stats = mtd->ecc_stats; |
730 | 746 | ||
731 | /* Read-while-load method */ | 747 | /* Read-while-load method */ |
@@ -754,9 +770,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
754 | * Now we issued chip 1 read and pointed chip 1 | 770 | * Now we issued chip 1 read and pointed chip 1 |
755 | * bufferam so we have to point chip 0 bufferam. | 771 | * bufferam so we have to point chip 0 bufferam. |
756 | */ | 772 | */ |
757 | if (this->device_id & ONENAND_DEVICE_IS_DDP && | 773 | if (ONENAND_IS_DDP(this) && |
758 | unlikely(from == (this->chipsize >> 1))) { | 774 | unlikely(from == (this->chipsize >> 1))) { |
759 | this->write_word(0, this->base + ONENAND_REG_START_ADDRESS2); | 775 | this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); |
760 | boundary = 1; | 776 | boundary = 1; |
761 | } else | 777 | } else |
762 | boundary = 0; | 778 | boundary = 0; |
@@ -770,7 +786,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
770 | break; | 786 | break; |
771 | /* Set up for next read from bufferRAM */ | 787 | /* Set up for next read from bufferRAM */ |
772 | if (unlikely(boundary)) | 788 | if (unlikely(boundary)) |
773 | this->write_word(0x8000, this->base + ONENAND_REG_START_ADDRESS2); | 789 | this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); |
774 | ONENAND_SET_NEXT_BUFFERRAM(this); | 790 | ONENAND_SET_NEXT_BUFFERRAM(this); |
775 | buf += thislen; | 791 | buf += thislen; |
776 | thislen = min_t(int, mtd->writesize, len - read); | 792 | thislen = min_t(int, mtd->writesize, len - read); |
@@ -801,20 +817,59 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
801 | } | 817 | } |
802 | 818 | ||
803 | /** | 819 | /** |
820 | * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer | ||
821 | * @param mtd MTD device structure | ||
822 | * @param buf destination address | ||
823 | * @param column oob offset to read from | ||
824 | * @param thislen oob length to read | ||
825 | */ | ||
826 | static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, | ||
827 | int thislen) | ||
828 | { | ||
829 | struct onenand_chip *this = mtd->priv; | ||
830 | struct nand_oobfree *free; | ||
831 | int readcol = column; | ||
832 | int readend = column + thislen; | ||
833 | int lastgap = 0; | ||
834 | uint8_t *oob_buf = this->page_buf + mtd->writesize; | ||
835 | |||
836 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
837 | if (readcol >= lastgap) | ||
838 | readcol += free->offset - lastgap; | ||
839 | if (readend >= lastgap) | ||
840 | readend += free->offset - lastgap; | ||
841 | lastgap = free->offset + free->length; | ||
842 | } | ||
843 | this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); | ||
844 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
845 | int free_end = free->offset + free->length; | ||
846 | if (free->offset < readend && free_end > readcol) { | ||
847 | int st = max_t(int,free->offset,readcol); | ||
848 | int ed = min_t(int,free_end,readend); | ||
849 | int n = ed - st; | ||
850 | memcpy(buf, oob_buf + st, n); | ||
851 | buf += n; | ||
852 | } | ||
853 | } | ||
854 | return 0; | ||
855 | } | ||
856 | |||
857 | /** | ||
804 | * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band | 858 | * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band |
805 | * @param mtd MTD device structure | 859 | * @param mtd MTD device structure |
806 | * @param from offset to read from | 860 | * @param from offset to read from |
807 | * @param len number of bytes to read | 861 | * @param len number of bytes to read |
808 | * @param retlen pointer to variable to store the number of read bytes | 862 | * @param retlen pointer to variable to store the number of read bytes |
809 | * @param buf the databuffer to put data | 863 | * @param buf the databuffer to put data |
864 | * @param mode operation mode | ||
810 | * | 865 | * |
811 | * OneNAND read out-of-band data from the spare area | 866 | * OneNAND read out-of-band data from the spare area |
812 | */ | 867 | */ |
813 | int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 868 | static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, |
814 | size_t *retlen, u_char *buf) | 869 | size_t *retlen, u_char *buf, mtd_oob_mode_t mode) |
815 | { | 870 | { |
816 | struct onenand_chip *this = mtd->priv; | 871 | struct onenand_chip *this = mtd->priv; |
817 | int read = 0, thislen, column; | 872 | int read = 0, thislen, column, oobsize; |
818 | int ret = 0; | 873 | int ret = 0; |
819 | 874 | ||
820 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | 875 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); |
@@ -822,21 +877,33 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
822 | /* Initialize return length value */ | 877 | /* Initialize return length value */ |
823 | *retlen = 0; | 878 | *retlen = 0; |
824 | 879 | ||
880 | if (mode == MTD_OOB_AUTO) | ||
881 | oobsize = this->ecclayout->oobavail; | ||
882 | else | ||
883 | oobsize = mtd->oobsize; | ||
884 | |||
885 | column = from & (mtd->oobsize - 1); | ||
886 | |||
887 | if (unlikely(column >= oobsize)) { | ||
888 | printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n"); | ||
889 | return -EINVAL; | ||
890 | } | ||
891 | |||
825 | /* Do not allow reads past end of device */ | 892 | /* Do not allow reads past end of device */ |
826 | if (unlikely((from + len) > mtd->size)) { | 893 | if (unlikely(from >= mtd->size || |
827 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); | 894 | column + len > ((mtd->size >> this->page_shift) - |
895 | (from >> this->page_shift)) * oobsize)) { | ||
896 | printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n"); | ||
828 | return -EINVAL; | 897 | return -EINVAL; |
829 | } | 898 | } |
830 | 899 | ||
831 | /* Grab the lock and see if the device is available */ | 900 | /* Grab the lock and see if the device is available */ |
832 | onenand_get_device(mtd, FL_READING); | 901 | onenand_get_device(mtd, FL_READING); |
833 | 902 | ||
834 | column = from & (mtd->oobsize - 1); | ||
835 | |||
836 | while (read < len) { | 903 | while (read < len) { |
837 | cond_resched(); | 904 | cond_resched(); |
838 | 905 | ||
839 | thislen = mtd->oobsize - column; | 906 | thislen = oobsize - column; |
840 | thislen = min_t(int, thislen, len); | 907 | thislen = min_t(int, thislen, len); |
841 | 908 | ||
842 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | 909 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); |
@@ -846,11 +913,14 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
846 | ret = this->wait(mtd, FL_READING); | 913 | ret = this->wait(mtd, FL_READING); |
847 | /* First copy data and check return value for ECC handling */ | 914 | /* First copy data and check return value for ECC handling */ |
848 | 915 | ||
849 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | 916 | if (mode == MTD_OOB_AUTO) |
917 | onenand_transfer_auto_oob(mtd, buf, column, thislen); | ||
918 | else | ||
919 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
850 | 920 | ||
851 | if (ret) { | 921 | if (ret) { |
852 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); | 922 | printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret); |
853 | goto out; | 923 | break; |
854 | } | 924 | } |
855 | 925 | ||
856 | read += thislen; | 926 | read += thislen; |
@@ -868,7 +938,6 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
868 | } | 938 | } |
869 | } | 939 | } |
870 | 940 | ||
871 | out: | ||
872 | /* Deselect and wake up anyone waiting on the device */ | 941 | /* Deselect and wake up anyone waiting on the device */ |
873 | onenand_release_device(mtd); | 942 | onenand_release_device(mtd); |
874 | 943 | ||
@@ -885,10 +954,132 @@ out: | |||
885 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | 954 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, |
886 | struct mtd_oob_ops *ops) | 955 | struct mtd_oob_ops *ops) |
887 | { | 956 | { |
888 | BUG_ON(ops->mode != MTD_OOB_PLACE); | 957 | switch (ops->mode) { |
889 | 958 | case MTD_OOB_PLACE: | |
959 | case MTD_OOB_AUTO: | ||
960 | break; | ||
961 | case MTD_OOB_RAW: | ||
962 | /* Not implemented yet */ | ||
963 | default: | ||
964 | return -EINVAL; | ||
965 | } | ||
890 | return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, | 966 | return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, |
891 | &ops->oobretlen, ops->oobbuf); | 967 | &ops->oobretlen, ops->oobbuf, ops->mode); |
968 | } | ||
969 | |||
970 | /** | ||
971 | * onenand_bbt_wait - [DEFAULT] wait until the command is done | ||
972 | * @param mtd MTD device structure | ||
973 | * @param state state to select the max. timeout value | ||
974 | * | ||
975 | * Wait for command done. | ||
976 | */ | ||
977 | static int onenand_bbt_wait(struct mtd_info *mtd, int state) | ||
978 | { | ||
979 | struct onenand_chip *this = mtd->priv; | ||
980 | unsigned long timeout; | ||
981 | unsigned int interrupt; | ||
982 | unsigned int ctrl; | ||
983 | |||
984 | /* The 20 msec is enough */ | ||
985 | timeout = jiffies + msecs_to_jiffies(20); | ||
986 | while (time_before(jiffies, timeout)) { | ||
987 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
988 | if (interrupt & ONENAND_INT_MASTER) | ||
989 | break; | ||
990 | } | ||
991 | /* To get correct interrupt status in timeout case */ | ||
992 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
993 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | ||
994 | |||
995 | if (ctrl & ONENAND_CTRL_ERROR) { | ||
996 | printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); | ||
997 | /* Initial bad block case */ | ||
998 | if (ctrl & ONENAND_CTRL_LOAD) | ||
999 | return ONENAND_BBT_READ_ERROR; | ||
1000 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
1001 | } | ||
1002 | |||
1003 | if (interrupt & ONENAND_INT_READ) { | ||
1004 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | ||
1005 | if (ecc & ONENAND_ECC_2BIT_ALL) | ||
1006 | return ONENAND_BBT_READ_ERROR; | ||
1007 | } else { | ||
1008 | printk(KERN_ERR "onenand_bbt_wait: read timeout!" | ||
1009 | "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); | ||
1010 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
1011 | } | ||
1012 | |||
1013 | return 0; | ||
1014 | } | ||
1015 | |||
1016 | /** | ||
1017 | * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan | ||
1018 | * @param mtd MTD device structure | ||
1019 | * @param from offset to read from | ||
1020 | * @param @ops oob operation description structure | ||
1021 | * | ||
1022 | * OneNAND read out-of-band data from the spare area for bbt scan | ||
1023 | */ | ||
1024 | int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | ||
1025 | struct mtd_oob_ops *ops) | ||
1026 | { | ||
1027 | struct onenand_chip *this = mtd->priv; | ||
1028 | int read = 0, thislen, column; | ||
1029 | int ret = 0; | ||
1030 | size_t len = ops->ooblen; | ||
1031 | u_char *buf = ops->oobbuf; | ||
1032 | |||
1033 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); | ||
1034 | |||
1035 | /* Initialize return value */ | ||
1036 | ops->oobretlen = 0; | ||
1037 | |||
1038 | /* Do not allow reads past end of device */ | ||
1039 | if (unlikely((from + len) > mtd->size)) { | ||
1040 | printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); | ||
1041 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
1042 | } | ||
1043 | |||
1044 | /* Grab the lock and see if the device is available */ | ||
1045 | onenand_get_device(mtd, FL_READING); | ||
1046 | |||
1047 | column = from & (mtd->oobsize - 1); | ||
1048 | |||
1049 | while (read < len) { | ||
1050 | cond_resched(); | ||
1051 | |||
1052 | thislen = mtd->oobsize - column; | ||
1053 | thislen = min_t(int, thislen, len); | ||
1054 | |||
1055 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | ||
1056 | |||
1057 | onenand_update_bufferram(mtd, from, 0); | ||
1058 | |||
1059 | ret = onenand_bbt_wait(mtd, FL_READING); | ||
1060 | if (ret) | ||
1061 | break; | ||
1062 | |||
1063 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
1064 | read += thislen; | ||
1065 | if (read == len) | ||
1066 | break; | ||
1067 | |||
1068 | buf += thislen; | ||
1069 | |||
1070 | /* Read more? */ | ||
1071 | if (read < len) { | ||
1072 | /* Update Page size */ | ||
1073 | from += mtd->writesize; | ||
1074 | column = 0; | ||
1075 | } | ||
1076 | } | ||
1077 | |||
1078 | /* Deselect and wake up anyone waiting on the device */ | ||
1079 | onenand_release_device(mtd); | ||
1080 | |||
1081 | ops->oobretlen = read; | ||
1082 | return ret; | ||
892 | } | 1083 | } |
893 | 1084 | ||
894 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE | 1085 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE |
@@ -897,14 +1088,12 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | |||
897 | * @param mtd MTD device structure | 1088 | * @param mtd MTD device structure |
898 | * @param buf the databuffer to verify | 1089 | * @param buf the databuffer to verify |
899 | * @param to offset to read from | 1090 | * @param to offset to read from |
900 | * @param len number of bytes to read and compare | ||
901 | * | 1091 | * |
902 | */ | 1092 | */ |
903 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len) | 1093 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) |
904 | { | 1094 | { |
905 | struct onenand_chip *this = mtd->priv; | 1095 | struct onenand_chip *this = mtd->priv; |
906 | char *readp = this->page_buf; | 1096 | char *readp = this->page_buf + mtd->writesize; |
907 | int column = to & (mtd->oobsize - 1); | ||
908 | int status, i; | 1097 | int status, i; |
909 | 1098 | ||
910 | this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); | 1099 | this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); |
@@ -913,9 +1102,8 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to | |||
913 | if (status) | 1102 | if (status) |
914 | return status; | 1103 | return status; |
915 | 1104 | ||
916 | this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len); | 1105 | this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize); |
917 | 1106 | for(i = 0; i < mtd->oobsize; i++) | |
918 | for(i = 0; i < len; i++) | ||
919 | if (buf[i] != 0xFF && buf[i] != readp[i]) | 1107 | if (buf[i] != 0xFF && buf[i] != readp[i]) |
920 | return -EBADMSG; | 1108 | return -EBADMSG; |
921 | 1109 | ||
@@ -923,41 +1111,51 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to | |||
923 | } | 1111 | } |
924 | 1112 | ||
925 | /** | 1113 | /** |
926 | * onenand_verify_page - [GENERIC] verify the chip contents after a write | 1114 | * onenand_verify - [GENERIC] verify the chip contents after a write |
927 | * @param mtd MTD device structure | 1115 | * @param mtd MTD device structure |
928 | * @param buf the databuffer to verify | 1116 | * @param buf the databuffer to verify |
1117 | * @param addr offset to read from | ||
1118 | * @param len number of bytes to read and compare | ||
929 | * | 1119 | * |
930 | * Check DataRAM area directly | ||
931 | */ | 1120 | */ |
932 | static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) | 1121 | static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) |
933 | { | 1122 | { |
934 | struct onenand_chip *this = mtd->priv; | 1123 | struct onenand_chip *this = mtd->priv; |
935 | void __iomem *dataram0, *dataram1; | 1124 | void __iomem *dataram; |
936 | int ret = 0; | 1125 | int ret = 0; |
1126 | int thislen, column; | ||
937 | 1127 | ||
938 | /* In partial page write, just skip it */ | 1128 | while (len != 0) { |
939 | if ((addr & (mtd->writesize - 1)) != 0) | 1129 | thislen = min_t(int, mtd->writesize, len); |
940 | return 0; | 1130 | column = addr & (mtd->writesize - 1); |
1131 | if (column + thislen > mtd->writesize) | ||
1132 | thislen = mtd->writesize - column; | ||
941 | 1133 | ||
942 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); | 1134 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); |
943 | 1135 | ||
944 | ret = this->wait(mtd, FL_READING); | 1136 | onenand_update_bufferram(mtd, addr, 0); |
945 | if (ret) | 1137 | |
946 | return ret; | 1138 | ret = this->wait(mtd, FL_READING); |
1139 | if (ret) | ||
1140 | return ret; | ||
947 | 1141 | ||
948 | onenand_update_bufferram(mtd, addr, 1); | 1142 | onenand_update_bufferram(mtd, addr, 1); |
949 | 1143 | ||
950 | /* Check, if the two dataram areas are same */ | 1144 | dataram = this->base + ONENAND_DATARAM; |
951 | dataram0 = this->base + ONENAND_DATARAM; | 1145 | dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); |
952 | dataram1 = dataram0 + mtd->writesize; | ||
953 | 1146 | ||
954 | if (memcmp(dataram0, dataram1, mtd->writesize)) | 1147 | if (memcmp(buf, dataram + column, thislen)) |
955 | return -EBADMSG; | 1148 | return -EBADMSG; |
1149 | |||
1150 | len -= thislen; | ||
1151 | buf += thislen; | ||
1152 | addr += thislen; | ||
1153 | } | ||
956 | 1154 | ||
957 | return 0; | 1155 | return 0; |
958 | } | 1156 | } |
959 | #else | 1157 | #else |
960 | #define onenand_verify_page(...) (0) | 1158 | #define onenand_verify(...) (0) |
961 | #define onenand_verify_oob(...) (0) | 1159 | #define onenand_verify_oob(...) (0) |
962 | #endif | 1160 | #endif |
963 | 1161 | ||
@@ -988,60 +1186,57 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
988 | 1186 | ||
989 | /* Do not allow writes past end of device */ | 1187 | /* Do not allow writes past end of device */ |
990 | if (unlikely((to + len) > mtd->size)) { | 1188 | if (unlikely((to + len) > mtd->size)) { |
991 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n"); | 1189 | printk(KERN_ERR "onenand_write: Attempt write to past end of device\n"); |
992 | return -EINVAL; | 1190 | return -EINVAL; |
993 | } | 1191 | } |
994 | 1192 | ||
995 | /* Reject writes, which are not page aligned */ | 1193 | /* Reject writes, which are not page aligned */ |
996 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { | 1194 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { |
997 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n"); | 1195 | printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n"); |
998 | return -EINVAL; | 1196 | return -EINVAL; |
999 | } | 1197 | } |
1000 | 1198 | ||
1001 | column = to & (mtd->writesize - 1); | 1199 | column = to & (mtd->writesize - 1); |
1002 | subpage = column || (len & (mtd->writesize - 1)); | ||
1003 | 1200 | ||
1004 | /* Grab the lock and see if the device is available */ | 1201 | /* Grab the lock and see if the device is available */ |
1005 | onenand_get_device(mtd, FL_WRITING); | 1202 | onenand_get_device(mtd, FL_WRITING); |
1006 | 1203 | ||
1007 | /* Loop until all data write */ | 1204 | /* Loop until all data write */ |
1008 | while (written < len) { | 1205 | while (written < len) { |
1009 | int bytes = mtd->writesize; | 1206 | int thislen = min_t(int, mtd->writesize - column, len - written); |
1010 | int thislen = min_t(int, bytes, len - written); | ||
1011 | u_char *wbuf = (u_char *) buf; | 1207 | u_char *wbuf = (u_char *) buf; |
1012 | 1208 | ||
1013 | cond_resched(); | 1209 | cond_resched(); |
1014 | 1210 | ||
1015 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes); | 1211 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); |
1016 | 1212 | ||
1017 | /* Partial page write */ | 1213 | /* Partial page write */ |
1214 | subpage = thislen < mtd->writesize; | ||
1018 | if (subpage) { | 1215 | if (subpage) { |
1019 | bytes = min_t(int, bytes - column, (int) len); | ||
1020 | memset(this->page_buf, 0xff, mtd->writesize); | 1216 | memset(this->page_buf, 0xff, mtd->writesize); |
1021 | memcpy(this->page_buf + column, buf, bytes); | 1217 | memcpy(this->page_buf + column, buf, thislen); |
1022 | wbuf = this->page_buf; | 1218 | wbuf = this->page_buf; |
1023 | /* Even though partial write, we need page size */ | ||
1024 | thislen = mtd->writesize; | ||
1025 | } | 1219 | } |
1026 | 1220 | ||
1027 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen); | 1221 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); |
1028 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | 1222 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); |
1029 | 1223 | ||
1030 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); | 1224 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); |
1031 | 1225 | ||
1226 | ret = this->wait(mtd, FL_WRITING); | ||
1227 | |||
1032 | /* In partial page write we don't update bufferram */ | 1228 | /* In partial page write we don't update bufferram */ |
1033 | onenand_update_bufferram(mtd, to, !subpage); | 1229 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
1034 | 1230 | ||
1035 | ret = this->wait(mtd, FL_WRITING); | ||
1036 | if (ret) { | 1231 | if (ret) { |
1037 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); | 1232 | printk(KERN_ERR "onenand_write: write filaed %d\n", ret); |
1038 | break; | 1233 | break; |
1039 | } | 1234 | } |
1040 | 1235 | ||
1041 | /* Only check verify write turn on */ | 1236 | /* Only check verify write turn on */ |
1042 | ret = onenand_verify_page(mtd, (u_char *) wbuf, to); | 1237 | ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); |
1043 | if (ret) { | 1238 | if (ret) { |
1044 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); | 1239 | printk(KERN_ERR "onenand_write: verify failed %d\n", ret); |
1045 | break; | 1240 | break; |
1046 | } | 1241 | } |
1047 | 1242 | ||
@@ -1064,20 +1259,58 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1064 | } | 1259 | } |
1065 | 1260 | ||
1066 | /** | 1261 | /** |
1262 | * onenand_fill_auto_oob - [Internal] oob auto-placement transfer | ||
1263 | * @param mtd MTD device structure | ||
1264 | * @param oob_buf oob buffer | ||
1265 | * @param buf source address | ||
1266 | * @param column oob offset to write to | ||
1267 | * @param thislen oob length to write | ||
1268 | */ | ||
1269 | static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, | ||
1270 | const u_char *buf, int column, int thislen) | ||
1271 | { | ||
1272 | struct onenand_chip *this = mtd->priv; | ||
1273 | struct nand_oobfree *free; | ||
1274 | int writecol = column; | ||
1275 | int writeend = column + thislen; | ||
1276 | int lastgap = 0; | ||
1277 | |||
1278 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
1279 | if (writecol >= lastgap) | ||
1280 | writecol += free->offset - lastgap; | ||
1281 | if (writeend >= lastgap) | ||
1282 | writeend += free->offset - lastgap; | ||
1283 | lastgap = free->offset + free->length; | ||
1284 | } | ||
1285 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
1286 | int free_end = free->offset + free->length; | ||
1287 | if (free->offset < writeend && free_end > writecol) { | ||
1288 | int st = max_t(int,free->offset,writecol); | ||
1289 | int ed = min_t(int,free_end,writeend); | ||
1290 | int n = ed - st; | ||
1291 | memcpy(oob_buf + st, buf, n); | ||
1292 | buf += n; | ||
1293 | } | ||
1294 | } | ||
1295 | return 0; | ||
1296 | } | ||
1297 | |||
1298 | /** | ||
1067 | * onenand_do_write_oob - [Internal] OneNAND write out-of-band | 1299 | * onenand_do_write_oob - [Internal] OneNAND write out-of-band |
1068 | * @param mtd MTD device structure | 1300 | * @param mtd MTD device structure |
1069 | * @param to offset to write to | 1301 | * @param to offset to write to |
1070 | * @param len number of bytes to write | 1302 | * @param len number of bytes to write |
1071 | * @param retlen pointer to variable to store the number of written bytes | 1303 | * @param retlen pointer to variable to store the number of written bytes |
1072 | * @param buf the data to write | 1304 | * @param buf the data to write |
1305 | * @param mode operation mode | ||
1073 | * | 1306 | * |
1074 | * OneNAND write out-of-band | 1307 | * OneNAND write out-of-band |
1075 | */ | 1308 | */ |
1076 | static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | 1309 | static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, |
1077 | size_t *retlen, const u_char *buf) | 1310 | size_t *retlen, const u_char *buf, mtd_oob_mode_t mode) |
1078 | { | 1311 | { |
1079 | struct onenand_chip *this = mtd->priv; | 1312 | struct onenand_chip *this = mtd->priv; |
1080 | int column, ret = 0; | 1313 | int column, ret = 0, oobsize; |
1081 | int written = 0; | 1314 | int written = 0; |
1082 | 1315 | ||
1083 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | 1316 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); |
@@ -1085,9 +1318,30 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1085 | /* Initialize retlen, in case of early exit */ | 1318 | /* Initialize retlen, in case of early exit */ |
1086 | *retlen = 0; | 1319 | *retlen = 0; |
1087 | 1320 | ||
1088 | /* Do not allow writes past end of device */ | 1321 | if (mode == MTD_OOB_AUTO) |
1089 | if (unlikely((to + len) > mtd->size)) { | 1322 | oobsize = this->ecclayout->oobavail; |
1090 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); | 1323 | else |
1324 | oobsize = mtd->oobsize; | ||
1325 | |||
1326 | column = to & (mtd->oobsize - 1); | ||
1327 | |||
1328 | if (unlikely(column >= oobsize)) { | ||
1329 | printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n"); | ||
1330 | return -EINVAL; | ||
1331 | } | ||
1332 | |||
1333 | /* For compatibility with NAND: Do not allow write past end of page */ | ||
1334 | if (column + len > oobsize) { | ||
1335 | printk(KERN_ERR "onenand_write_oob: " | ||
1336 | "Attempt to write past end of page\n"); | ||
1337 | return -EINVAL; | ||
1338 | } | ||
1339 | |||
1340 | /* Do not allow reads past end of device */ | ||
1341 | if (unlikely(to >= mtd->size || | ||
1342 | column + len > ((mtd->size >> this->page_shift) - | ||
1343 | (to >> this->page_shift)) * oobsize)) { | ||
1344 | printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n"); | ||
1091 | return -EINVAL; | 1345 | return -EINVAL; |
1092 | } | 1346 | } |
1093 | 1347 | ||
@@ -1096,18 +1350,19 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1096 | 1350 | ||
1097 | /* Loop until all data write */ | 1351 | /* Loop until all data write */ |
1098 | while (written < len) { | 1352 | while (written < len) { |
1099 | int thislen = min_t(int, mtd->oobsize, len - written); | 1353 | int thislen = min_t(int, oobsize, len - written); |
1100 | 1354 | ||
1101 | cond_resched(); | 1355 | cond_resched(); |
1102 | 1356 | ||
1103 | column = to & (mtd->oobsize - 1); | ||
1104 | |||
1105 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); | 1357 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); |
1106 | 1358 | ||
1107 | /* We send data to spare ram with oobsize | 1359 | /* We send data to spare ram with oobsize |
1108 | * to prevent byte access */ | 1360 | * to prevent byte access */ |
1109 | memset(this->page_buf, 0xff, mtd->oobsize); | 1361 | memset(this->page_buf, 0xff, mtd->oobsize); |
1110 | memcpy(this->page_buf + column, buf, thislen); | 1362 | if (mode == MTD_OOB_AUTO) |
1363 | onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen); | ||
1364 | else | ||
1365 | memcpy(this->page_buf + column, buf, thislen); | ||
1111 | this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); | 1366 | this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); |
1112 | 1367 | ||
1113 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | 1368 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); |
@@ -1116,26 +1371,25 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1116 | 1371 | ||
1117 | ret = this->wait(mtd, FL_WRITING); | 1372 | ret = this->wait(mtd, FL_WRITING); |
1118 | if (ret) { | 1373 | if (ret) { |
1119 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret); | 1374 | printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret); |
1120 | goto out; | 1375 | break; |
1121 | } | 1376 | } |
1122 | 1377 | ||
1123 | ret = onenand_verify_oob(mtd, buf, to, thislen); | 1378 | ret = onenand_verify_oob(mtd, this->page_buf, to); |
1124 | if (ret) { | 1379 | if (ret) { |
1125 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); | 1380 | printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); |
1126 | goto out; | 1381 | break; |
1127 | } | 1382 | } |
1128 | 1383 | ||
1129 | written += thislen; | 1384 | written += thislen; |
1130 | |||
1131 | if (written == len) | 1385 | if (written == len) |
1132 | break; | 1386 | break; |
1133 | 1387 | ||
1134 | to += thislen; | 1388 | to += mtd->writesize; |
1135 | buf += thislen; | 1389 | buf += thislen; |
1390 | column = 0; | ||
1136 | } | 1391 | } |
1137 | 1392 | ||
1138 | out: | ||
1139 | /* Deselect and wake up anyone waiting on the device */ | 1393 | /* Deselect and wake up anyone waiting on the device */ |
1140 | onenand_release_device(mtd); | 1394 | onenand_release_device(mtd); |
1141 | 1395 | ||
@@ -1153,10 +1407,17 @@ out: | |||
1153 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, | 1407 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, |
1154 | struct mtd_oob_ops *ops) | 1408 | struct mtd_oob_ops *ops) |
1155 | { | 1409 | { |
1156 | BUG_ON(ops->mode != MTD_OOB_PLACE); | 1410 | switch (ops->mode) { |
1157 | 1411 | case MTD_OOB_PLACE: | |
1412 | case MTD_OOB_AUTO: | ||
1413 | break; | ||
1414 | case MTD_OOB_RAW: | ||
1415 | /* Not implemented yet */ | ||
1416 | default: | ||
1417 | return -EINVAL; | ||
1418 | } | ||
1158 | return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, | 1419 | return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, |
1159 | &ops->oobretlen, ops->oobbuf); | 1420 | &ops->oobretlen, ops->oobbuf, ops->mode); |
1160 | } | 1421 | } |
1161 | 1422 | ||
1162 | /** | 1423 | /** |
@@ -1199,19 +1460,19 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1199 | 1460 | ||
1200 | /* Start address must align on block boundary */ | 1461 | /* Start address must align on block boundary */ |
1201 | if (unlikely(instr->addr & (block_size - 1))) { | 1462 | if (unlikely(instr->addr & (block_size - 1))) { |
1202 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); | 1463 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); |
1203 | return -EINVAL; | 1464 | return -EINVAL; |
1204 | } | 1465 | } |
1205 | 1466 | ||
1206 | /* Length must align on block boundary */ | 1467 | /* Length must align on block boundary */ |
1207 | if (unlikely(instr->len & (block_size - 1))) { | 1468 | if (unlikely(instr->len & (block_size - 1))) { |
1208 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); | 1469 | printk(KERN_ERR "onenand_erase: Length not block aligned\n"); |
1209 | return -EINVAL; | 1470 | return -EINVAL; |
1210 | } | 1471 | } |
1211 | 1472 | ||
1212 | /* Do not allow erase past end of device */ | 1473 | /* Do not allow erase past end of device */ |
1213 | if (unlikely((instr->len + instr->addr) > mtd->size)) { | 1474 | if (unlikely((instr->len + instr->addr) > mtd->size)) { |
1214 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); | 1475 | printk(KERN_ERR "onenand_erase: Erase past end of device\n"); |
1215 | return -EINVAL; | 1476 | return -EINVAL; |
1216 | } | 1477 | } |
1217 | 1478 | ||
@@ -1238,10 +1499,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1238 | 1499 | ||
1239 | this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); | 1500 | this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); |
1240 | 1501 | ||
1502 | onenand_invalidate_bufferram(mtd, addr, block_size); | ||
1503 | |||
1241 | ret = this->wait(mtd, FL_ERASING); | 1504 | ret = this->wait(mtd, FL_ERASING); |
1242 | /* Check, if it is write protected */ | 1505 | /* Check, if it is write protected */ |
1243 | if (ret) { | 1506 | if (ret) { |
1244 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); | 1507 | printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); |
1245 | instr->state = MTD_ERASE_FAILED; | 1508 | instr->state = MTD_ERASE_FAILED; |
1246 | instr->fail_addr = addr; | 1509 | instr->fail_addr = addr; |
1247 | goto erase_exit; | 1510 | goto erase_exit; |
@@ -1322,7 +1585,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1322 | 1585 | ||
1323 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 1586 | /* We write two bytes, so we dont have to mess with 16 bit access */ |
1324 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | 1587 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); |
1325 | return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); | 1588 | return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE); |
1326 | } | 1589 | } |
1327 | 1590 | ||
1328 | /** | 1591 | /** |
@@ -1491,6 +1754,8 @@ static int onenand_unlock_all(struct mtd_info *mtd) | |||
1491 | struct onenand_chip *this = mtd->priv; | 1754 | struct onenand_chip *this = mtd->priv; |
1492 | 1755 | ||
1493 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { | 1756 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { |
1757 | /* Set start block address */ | ||
1758 | this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | ||
1494 | /* Write unlock command */ | 1759 | /* Write unlock command */ |
1495 | this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); | 1760 | this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); |
1496 | 1761 | ||
@@ -1503,13 +1768,10 @@ static int onenand_unlock_all(struct mtd_info *mtd) | |||
1503 | continue; | 1768 | continue; |
1504 | 1769 | ||
1505 | /* Workaround for all block unlock in DDP */ | 1770 | /* Workaround for all block unlock in DDP */ |
1506 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | 1771 | if (ONENAND_IS_DDP(this)) { |
1507 | loff_t ofs; | ||
1508 | size_t len; | ||
1509 | |||
1510 | /* 1st block on another chip */ | 1772 | /* 1st block on another chip */ |
1511 | ofs = this->chipsize >> 1; | 1773 | loff_t ofs = this->chipsize >> 1; |
1512 | len = 1 << this->erase_shift; | 1774 | size_t len = mtd->erasesize; |
1513 | 1775 | ||
1514 | onenand_unlock(mtd, ofs, len); | 1776 | onenand_unlock(mtd, ofs, len); |
1515 | } | 1777 | } |
@@ -1617,7 +1879,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, | |||
1617 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); | 1879 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
1618 | this->wait(mtd, FL_OTPING); | 1880 | this->wait(mtd, FL_OTPING); |
1619 | 1881 | ||
1620 | ret = onenand_do_write_oob(mtd, from, len, retlen, buf); | 1882 | ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE); |
1621 | 1883 | ||
1622 | /* Exit OTP access mode */ | 1884 | /* Exit OTP access mode */ |
1623 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 1885 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
@@ -1823,12 +2085,13 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | |||
1823 | #endif /* CONFIG_MTD_ONENAND_OTP */ | 2085 | #endif /* CONFIG_MTD_ONENAND_OTP */ |
1824 | 2086 | ||
1825 | /** | 2087 | /** |
1826 | * onenand_lock_scheme - Check and set OneNAND lock scheme | 2088 | * onenand_check_features - Check and set OneNAND features |
1827 | * @param mtd MTD data structure | 2089 | * @param mtd MTD data structure |
1828 | * | 2090 | * |
1829 | * Check and set OneNAND lock scheme | 2091 | * Check and set OneNAND features |
2092 | * - lock scheme | ||
1830 | */ | 2093 | */ |
1831 | static void onenand_lock_scheme(struct mtd_info *mtd) | 2094 | static void onenand_check_features(struct mtd_info *mtd) |
1832 | { | 2095 | { |
1833 | struct onenand_chip *this = mtd->priv; | 2096 | struct onenand_chip *this = mtd->priv; |
1834 | unsigned int density, process; | 2097 | unsigned int density, process; |
@@ -1961,26 +2224,28 @@ static int onenand_probe(struct mtd_info *mtd) | |||
1961 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; | 2224 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; |
1962 | this->chipsize = (16 << density) << 20; | 2225 | this->chipsize = (16 << density) << 20; |
1963 | /* Set density mask. it is used for DDP */ | 2226 | /* Set density mask. it is used for DDP */ |
1964 | this->density_mask = (1 << (density + 6)); | 2227 | if (ONENAND_IS_DDP(this)) |
2228 | this->density_mask = (1 << (density + 6)); | ||
2229 | else | ||
2230 | this->density_mask = 0; | ||
1965 | 2231 | ||
1966 | /* OneNAND page size & block size */ | 2232 | /* OneNAND page size & block size */ |
1967 | /* The data buffer size is equal to page size */ | 2233 | /* The data buffer size is equal to page size */ |
1968 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); | 2234 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); |
1969 | mtd->oobsize = mtd->writesize >> 5; | 2235 | mtd->oobsize = mtd->writesize >> 5; |
1970 | /* Pagers per block is always 64 in OneNAND */ | 2236 | /* Pages per a block are always 64 in OneNAND */ |
1971 | mtd->erasesize = mtd->writesize << 6; | 2237 | mtd->erasesize = mtd->writesize << 6; |
1972 | 2238 | ||
1973 | this->erase_shift = ffs(mtd->erasesize) - 1; | 2239 | this->erase_shift = ffs(mtd->erasesize) - 1; |
1974 | this->page_shift = ffs(mtd->writesize) - 1; | 2240 | this->page_shift = ffs(mtd->writesize) - 1; |
1975 | this->ppb_shift = (this->erase_shift - this->page_shift); | 2241 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; |
1976 | this->page_mask = (mtd->erasesize / mtd->writesize) - 1; | ||
1977 | 2242 | ||
1978 | /* REVIST: Multichip handling */ | 2243 | /* REVIST: Multichip handling */ |
1979 | 2244 | ||
1980 | mtd->size = this->chipsize; | 2245 | mtd->size = this->chipsize; |
1981 | 2246 | ||
1982 | /* Check OneNAND lock scheme */ | 2247 | /* Check OneNAND features */ |
1983 | onenand_lock_scheme(mtd); | 2248 | onenand_check_features(mtd); |
1984 | 2249 | ||
1985 | return 0; | 2250 | return 0; |
1986 | } | 2251 | } |
@@ -2021,6 +2286,7 @@ static void onenand_resume(struct mtd_info *mtd) | |||
2021 | */ | 2286 | */ |
2022 | int onenand_scan(struct mtd_info *mtd, int maxchips) | 2287 | int onenand_scan(struct mtd_info *mtd, int maxchips) |
2023 | { | 2288 | { |
2289 | int i; | ||
2024 | struct onenand_chip *this = mtd->priv; | 2290 | struct onenand_chip *this = mtd->priv; |
2025 | 2291 | ||
2026 | if (!this->read_word) | 2292 | if (!this->read_word) |
@@ -2092,12 +2358,21 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2092 | } | 2358 | } |
2093 | 2359 | ||
2094 | this->subpagesize = mtd->writesize >> mtd->subpage_sft; | 2360 | this->subpagesize = mtd->writesize >> mtd->subpage_sft; |
2361 | |||
2362 | /* | ||
2363 | * The number of bytes available for a client to place data into | ||
2364 | * the out of band area | ||
2365 | */ | ||
2366 | this->ecclayout->oobavail = 0; | ||
2367 | for (i = 0; this->ecclayout->oobfree[i].length; i++) | ||
2368 | this->ecclayout->oobavail += | ||
2369 | this->ecclayout->oobfree[i].length; | ||
2370 | |||
2095 | mtd->ecclayout = this->ecclayout; | 2371 | mtd->ecclayout = this->ecclayout; |
2096 | 2372 | ||
2097 | /* Fill in remaining MTD driver data */ | 2373 | /* Fill in remaining MTD driver data */ |
2098 | mtd->type = MTD_NANDFLASH; | 2374 | mtd->type = MTD_NANDFLASH; |
2099 | mtd->flags = MTD_CAP_NANDFLASH; | 2375 | mtd->flags = MTD_CAP_NANDFLASH; |
2100 | mtd->ecctype = MTD_ECC_SW; | ||
2101 | mtd->erase = onenand_erase; | 2376 | mtd->erase = onenand_erase; |
2102 | mtd->point = NULL; | 2377 | mtd->point = NULL; |
2103 | mtd->unpoint = NULL; | 2378 | mtd->unpoint = NULL; |
@@ -2144,8 +2419,11 @@ void onenand_release(struct mtd_info *mtd) | |||
2144 | del_mtd_device (mtd); | 2419 | del_mtd_device (mtd); |
2145 | 2420 | ||
2146 | /* Free bad block table memory, if allocated */ | 2421 | /* Free bad block table memory, if allocated */ |
2147 | if (this->bbm) | 2422 | if (this->bbm) { |
2423 | struct bbm_info *bbm = this->bbm; | ||
2424 | kfree(bbm->bbt); | ||
2148 | kfree(this->bbm); | 2425 | kfree(this->bbm); |
2426 | } | ||
2149 | /* Buffer allocated by onenand_scan */ | 2427 | /* Buffer allocated by onenand_scan */ |
2150 | if (this->options & ONENAND_PAGEBUF_ALLOC) | 2428 | if (this->options & ONENAND_PAGEBUF_ALLOC) |
2151 | kfree(this->page_buf); | 2429 | kfree(this->page_buf); |