diff options
author | David Woodhouse <dwmw2@infradead.org> | 2007-02-09 07:36:22 -0500 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2007-02-09 07:36:22 -0500 |
commit | c1f1625860847b57a0450a28d112423c2af675ff (patch) | |
tree | 2390866229b7d0eeb61166e77b153c8d3c70ee3a | |
parent | 552a8278d0becd671274af56a11ee601e585387a (diff) | |
parent | cde36b37d6fa5ebc8c95461a972c379185626b2c (diff) |
Merge branch 'master' of git://git.infradead.org/~kmpark/onenand-mtd-2.6
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 572 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 27 | ||||
-rw-r--r-- | include/linux/mtd/bbm.h | 7 | ||||
-rw-r--r-- | include/linux/mtd/onenand.h | 15 | ||||
-rw-r--r-- | include/linux/mtd/onenand_regs.h | 7 |
5 files changed, 446 insertions, 182 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 2da6bb26353e..779327b845d1 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,26 @@ 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 |
639 | 635 | this->bufferram[i].blockpage = -1; | |
640 | return 0; | ||
641 | } | 636 | } |
642 | 637 | ||
643 | /** | 638 | /** |
@@ -716,7 +711,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
716 | 711 | ||
717 | /* Do not allow reads past end of device */ | 712 | /* Do not allow reads past end of device */ |
718 | if ((from + len) > mtd->size) { | 713 | if ((from + len) > mtd->size) { |
719 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n"); | 714 | printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n"); |
720 | *retlen = 0; | 715 | *retlen = 0; |
721 | return -EINVAL; | 716 | return -EINVAL; |
722 | } | 717 | } |
@@ -724,8 +719,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 */ | 719 | /* Grab the lock and see if the device is available */ |
725 | onenand_get_device(mtd, FL_READING); | 720 | onenand_get_device(mtd, FL_READING); |
726 | 721 | ||
727 | /* TODO handling oob */ | ||
728 | |||
729 | stats = mtd->ecc_stats; | 722 | stats = mtd->ecc_stats; |
730 | 723 | ||
731 | /* Read-while-load method */ | 724 | /* Read-while-load method */ |
@@ -754,9 +747,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 | 747 | * Now we issued chip 1 read and pointed chip 1 |
755 | * bufferam so we have to point chip 0 bufferam. | 748 | * bufferam so we have to point chip 0 bufferam. |
756 | */ | 749 | */ |
757 | if (this->device_id & ONENAND_DEVICE_IS_DDP && | 750 | if (ONENAND_IS_DDP(this) && |
758 | unlikely(from == (this->chipsize >> 1))) { | 751 | unlikely(from == (this->chipsize >> 1))) { |
759 | this->write_word(0, this->base + ONENAND_REG_START_ADDRESS2); | 752 | this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); |
760 | boundary = 1; | 753 | boundary = 1; |
761 | } else | 754 | } else |
762 | boundary = 0; | 755 | boundary = 0; |
@@ -770,7 +763,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
770 | break; | 763 | break; |
771 | /* Set up for next read from bufferRAM */ | 764 | /* Set up for next read from bufferRAM */ |
772 | if (unlikely(boundary)) | 765 | if (unlikely(boundary)) |
773 | this->write_word(0x8000, this->base + ONENAND_REG_START_ADDRESS2); | 766 | this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); |
774 | ONENAND_SET_NEXT_BUFFERRAM(this); | 767 | ONENAND_SET_NEXT_BUFFERRAM(this); |
775 | buf += thislen; | 768 | buf += thislen; |
776 | thislen = min_t(int, mtd->writesize, len - read); | 769 | thislen = min_t(int, mtd->writesize, len - read); |
@@ -801,20 +794,59 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
801 | } | 794 | } |
802 | 795 | ||
803 | /** | 796 | /** |
797 | * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer | ||
798 | * @param mtd MTD device structure | ||
799 | * @param buf destination address | ||
800 | * @param column oob offset to read from | ||
801 | * @param thislen oob length to read | ||
802 | */ | ||
803 | static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, | ||
804 | int thislen) | ||
805 | { | ||
806 | struct onenand_chip *this = mtd->priv; | ||
807 | struct nand_oobfree *free; | ||
808 | int readcol = column; | ||
809 | int readend = column + thislen; | ||
810 | int lastgap = 0; | ||
811 | uint8_t *oob_buf = this->page_buf + mtd->writesize; | ||
812 | |||
813 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
814 | if (readcol >= lastgap) | ||
815 | readcol += free->offset - lastgap; | ||
816 | if (readend >= lastgap) | ||
817 | readend += free->offset - lastgap; | ||
818 | lastgap = free->offset + free->length; | ||
819 | } | ||
820 | this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); | ||
821 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
822 | int free_end = free->offset + free->length; | ||
823 | if (free->offset < readend && free_end > readcol) { | ||
824 | int st = max_t(int,free->offset,readcol); | ||
825 | int ed = min_t(int,free_end,readend); | ||
826 | int n = ed - st; | ||
827 | memcpy(buf, oob_buf + st, n); | ||
828 | buf += n; | ||
829 | } | ||
830 | } | ||
831 | return 0; | ||
832 | } | ||
833 | |||
834 | /** | ||
804 | * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band | 835 | * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band |
805 | * @param mtd MTD device structure | 836 | * @param mtd MTD device structure |
806 | * @param from offset to read from | 837 | * @param from offset to read from |
807 | * @param len number of bytes to read | 838 | * @param len number of bytes to read |
808 | * @param retlen pointer to variable to store the number of read bytes | 839 | * @param retlen pointer to variable to store the number of read bytes |
809 | * @param buf the databuffer to put data | 840 | * @param buf the databuffer to put data |
841 | * @param mode operation mode | ||
810 | * | 842 | * |
811 | * OneNAND read out-of-band data from the spare area | 843 | * OneNAND read out-of-band data from the spare area |
812 | */ | 844 | */ |
813 | int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 845 | static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, |
814 | size_t *retlen, u_char *buf) | 846 | size_t *retlen, u_char *buf, mtd_oob_mode_t mode) |
815 | { | 847 | { |
816 | struct onenand_chip *this = mtd->priv; | 848 | struct onenand_chip *this = mtd->priv; |
817 | int read = 0, thislen, column; | 849 | int read = 0, thislen, column, oobsize; |
818 | int ret = 0; | 850 | int ret = 0; |
819 | 851 | ||
820 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | 852 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); |
@@ -822,21 +854,33 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
822 | /* Initialize return length value */ | 854 | /* Initialize return length value */ |
823 | *retlen = 0; | 855 | *retlen = 0; |
824 | 856 | ||
857 | if (mode == MTD_OOB_AUTO) | ||
858 | oobsize = this->ecclayout->oobavail; | ||
859 | else | ||
860 | oobsize = mtd->oobsize; | ||
861 | |||
862 | column = from & (mtd->oobsize - 1); | ||
863 | |||
864 | if (unlikely(column >= oobsize)) { | ||
865 | printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n"); | ||
866 | return -EINVAL; | ||
867 | } | ||
868 | |||
825 | /* Do not allow reads past end of device */ | 869 | /* Do not allow reads past end of device */ |
826 | if (unlikely((from + len) > mtd->size)) { | 870 | if (unlikely(from >= mtd->size || |
827 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); | 871 | column + len > ((mtd->size >> this->page_shift) - |
872 | (from >> this->page_shift)) * oobsize)) { | ||
873 | printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n"); | ||
828 | return -EINVAL; | 874 | return -EINVAL; |
829 | } | 875 | } |
830 | 876 | ||
831 | /* Grab the lock and see if the device is available */ | 877 | /* Grab the lock and see if the device is available */ |
832 | onenand_get_device(mtd, FL_READING); | 878 | onenand_get_device(mtd, FL_READING); |
833 | 879 | ||
834 | column = from & (mtd->oobsize - 1); | ||
835 | |||
836 | while (read < len) { | 880 | while (read < len) { |
837 | cond_resched(); | 881 | cond_resched(); |
838 | 882 | ||
839 | thislen = mtd->oobsize - column; | 883 | thislen = oobsize - column; |
840 | thislen = min_t(int, thislen, len); | 884 | thislen = min_t(int, thislen, len); |
841 | 885 | ||
842 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | 886 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); |
@@ -846,11 +890,14 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
846 | ret = this->wait(mtd, FL_READING); | 890 | ret = this->wait(mtd, FL_READING); |
847 | /* First copy data and check return value for ECC handling */ | 891 | /* First copy data and check return value for ECC handling */ |
848 | 892 | ||
849 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | 893 | if (mode == MTD_OOB_AUTO) |
894 | onenand_transfer_auto_oob(mtd, buf, column, thislen); | ||
895 | else | ||
896 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
850 | 897 | ||
851 | if (ret) { | 898 | if (ret) { |
852 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); | 899 | printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret); |
853 | goto out; | 900 | break; |
854 | } | 901 | } |
855 | 902 | ||
856 | read += thislen; | 903 | read += thislen; |
@@ -868,7 +915,6 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
868 | } | 915 | } |
869 | } | 916 | } |
870 | 917 | ||
871 | out: | ||
872 | /* Deselect and wake up anyone waiting on the device */ | 918 | /* Deselect and wake up anyone waiting on the device */ |
873 | onenand_release_device(mtd); | 919 | onenand_release_device(mtd); |
874 | 920 | ||
@@ -885,10 +931,132 @@ out: | |||
885 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | 931 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, |
886 | struct mtd_oob_ops *ops) | 932 | struct mtd_oob_ops *ops) |
887 | { | 933 | { |
888 | BUG_ON(ops->mode != MTD_OOB_PLACE); | 934 | switch (ops->mode) { |
889 | 935 | case MTD_OOB_PLACE: | |
936 | case MTD_OOB_AUTO: | ||
937 | break; | ||
938 | case MTD_OOB_RAW: | ||
939 | /* Not implemented yet */ | ||
940 | default: | ||
941 | return -EINVAL; | ||
942 | } | ||
890 | return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, | 943 | return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, |
891 | &ops->oobretlen, ops->oobbuf); | 944 | &ops->oobretlen, ops->oobbuf, ops->mode); |
945 | } | ||
946 | |||
947 | /** | ||
948 | * onenand_bbt_wait - [DEFAULT] wait until the command is done | ||
949 | * @param mtd MTD device structure | ||
950 | * @param state state to select the max. timeout value | ||
951 | * | ||
952 | * Wait for command done. | ||
953 | */ | ||
954 | static int onenand_bbt_wait(struct mtd_info *mtd, int state) | ||
955 | { | ||
956 | struct onenand_chip *this = mtd->priv; | ||
957 | unsigned long timeout; | ||
958 | unsigned int interrupt; | ||
959 | unsigned int ctrl; | ||
960 | |||
961 | /* The 20 msec is enough */ | ||
962 | timeout = jiffies + msecs_to_jiffies(20); | ||
963 | while (time_before(jiffies, timeout)) { | ||
964 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
965 | if (interrupt & ONENAND_INT_MASTER) | ||
966 | break; | ||
967 | } | ||
968 | /* To get correct interrupt status in timeout case */ | ||
969 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
970 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | ||
971 | |||
972 | if (ctrl & ONENAND_CTRL_ERROR) { | ||
973 | printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); | ||
974 | /* Initial bad block case */ | ||
975 | if (ctrl & ONENAND_CTRL_LOAD) | ||
976 | return ONENAND_BBT_READ_ERROR; | ||
977 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
978 | } | ||
979 | |||
980 | if (interrupt & ONENAND_INT_READ) { | ||
981 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | ||
982 | if (ecc & ONENAND_ECC_2BIT_ALL) | ||
983 | return ONENAND_BBT_READ_ERROR; | ||
984 | } else { | ||
985 | printk(KERN_ERR "onenand_bbt_wait: read timeout!" | ||
986 | "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); | ||
987 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
988 | } | ||
989 | |||
990 | return 0; | ||
991 | } | ||
992 | |||
993 | /** | ||
994 | * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan | ||
995 | * @param mtd MTD device structure | ||
996 | * @param from offset to read from | ||
997 | * @param @ops oob operation description structure | ||
998 | * | ||
999 | * OneNAND read out-of-band data from the spare area for bbt scan | ||
1000 | */ | ||
1001 | int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | ||
1002 | struct mtd_oob_ops *ops) | ||
1003 | { | ||
1004 | struct onenand_chip *this = mtd->priv; | ||
1005 | int read = 0, thislen, column; | ||
1006 | int ret = 0; | ||
1007 | size_t len = ops->ooblen; | ||
1008 | u_char *buf = ops->oobbuf; | ||
1009 | |||
1010 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, len); | ||
1011 | |||
1012 | /* Initialize return value */ | ||
1013 | ops->oobretlen = 0; | ||
1014 | |||
1015 | /* Do not allow reads past end of device */ | ||
1016 | if (unlikely((from + len) > mtd->size)) { | ||
1017 | printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); | ||
1018 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
1019 | } | ||
1020 | |||
1021 | /* Grab the lock and see if the device is available */ | ||
1022 | onenand_get_device(mtd, FL_READING); | ||
1023 | |||
1024 | column = from & (mtd->oobsize - 1); | ||
1025 | |||
1026 | while (read < len) { | ||
1027 | cond_resched(); | ||
1028 | |||
1029 | thislen = mtd->oobsize - column; | ||
1030 | thislen = min_t(int, thislen, len); | ||
1031 | |||
1032 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | ||
1033 | |||
1034 | onenand_update_bufferram(mtd, from, 0); | ||
1035 | |||
1036 | ret = onenand_bbt_wait(mtd, FL_READING); | ||
1037 | if (ret) | ||
1038 | break; | ||
1039 | |||
1040 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
1041 | read += thislen; | ||
1042 | if (read == len) | ||
1043 | break; | ||
1044 | |||
1045 | buf += thislen; | ||
1046 | |||
1047 | /* Read more? */ | ||
1048 | if (read < len) { | ||
1049 | /* Update Page size */ | ||
1050 | from += mtd->writesize; | ||
1051 | column = 0; | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | /* Deselect and wake up anyone waiting on the device */ | ||
1056 | onenand_release_device(mtd); | ||
1057 | |||
1058 | ops->oobretlen = read; | ||
1059 | return ret; | ||
892 | } | 1060 | } |
893 | 1061 | ||
894 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE | 1062 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE |
@@ -897,14 +1065,12 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | |||
897 | * @param mtd MTD device structure | 1065 | * @param mtd MTD device structure |
898 | * @param buf the databuffer to verify | 1066 | * @param buf the databuffer to verify |
899 | * @param to offset to read from | 1067 | * @param to offset to read from |
900 | * @param len number of bytes to read and compare | ||
901 | * | 1068 | * |
902 | */ | 1069 | */ |
903 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len) | 1070 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) |
904 | { | 1071 | { |
905 | struct onenand_chip *this = mtd->priv; | 1072 | struct onenand_chip *this = mtd->priv; |
906 | char *readp = this->page_buf; | 1073 | char *readp = this->page_buf + mtd->writesize; |
907 | int column = to & (mtd->oobsize - 1); | ||
908 | int status, i; | 1074 | int status, i; |
909 | 1075 | ||
910 | this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); | 1076 | this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); |
@@ -913,9 +1079,8 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to | |||
913 | if (status) | 1079 | if (status) |
914 | return status; | 1080 | return status; |
915 | 1081 | ||
916 | this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len); | 1082 | this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize); |
917 | 1083 | for(i = 0; i < mtd->oobsize; i++) | |
918 | for(i = 0; i < len; i++) | ||
919 | if (buf[i] != 0xFF && buf[i] != readp[i]) | 1084 | if (buf[i] != 0xFF && buf[i] != readp[i]) |
920 | return -EBADMSG; | 1085 | return -EBADMSG; |
921 | 1086 | ||
@@ -923,41 +1088,51 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to | |||
923 | } | 1088 | } |
924 | 1089 | ||
925 | /** | 1090 | /** |
926 | * onenand_verify_page - [GENERIC] verify the chip contents after a write | 1091 | * onenand_verify - [GENERIC] verify the chip contents after a write |
927 | * @param mtd MTD device structure | 1092 | * @param mtd MTD device structure |
928 | * @param buf the databuffer to verify | 1093 | * @param buf the databuffer to verify |
1094 | * @param addr offset to read from | ||
1095 | * @param len number of bytes to read and compare | ||
929 | * | 1096 | * |
930 | * Check DataRAM area directly | ||
931 | */ | 1097 | */ |
932 | static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) | 1098 | static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) |
933 | { | 1099 | { |
934 | struct onenand_chip *this = mtd->priv; | 1100 | struct onenand_chip *this = mtd->priv; |
935 | void __iomem *dataram0, *dataram1; | 1101 | void __iomem *dataram; |
936 | int ret = 0; | 1102 | int ret = 0; |
1103 | int thislen, column; | ||
937 | 1104 | ||
938 | /* In partial page write, just skip it */ | 1105 | while (len != 0) { |
939 | if ((addr & (mtd->writesize - 1)) != 0) | 1106 | thislen = min_t(int, mtd->writesize, len); |
940 | return 0; | 1107 | column = addr & (mtd->writesize - 1); |
1108 | if (column + thislen > mtd->writesize) | ||
1109 | thislen = mtd->writesize - column; | ||
941 | 1110 | ||
942 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); | 1111 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); |
943 | 1112 | ||
944 | ret = this->wait(mtd, FL_READING); | 1113 | onenand_update_bufferram(mtd, addr, 0); |
945 | if (ret) | 1114 | |
946 | return ret; | 1115 | ret = this->wait(mtd, FL_READING); |
1116 | if (ret) | ||
1117 | return ret; | ||
947 | 1118 | ||
948 | onenand_update_bufferram(mtd, addr, 1); | 1119 | onenand_update_bufferram(mtd, addr, 1); |
949 | 1120 | ||
950 | /* Check, if the two dataram areas are same */ | 1121 | dataram = this->base + ONENAND_DATARAM; |
951 | dataram0 = this->base + ONENAND_DATARAM; | 1122 | dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); |
952 | dataram1 = dataram0 + mtd->writesize; | ||
953 | 1123 | ||
954 | if (memcmp(dataram0, dataram1, mtd->writesize)) | 1124 | if (memcmp(buf, dataram + column, thislen)) |
955 | return -EBADMSG; | 1125 | return -EBADMSG; |
1126 | |||
1127 | len -= thislen; | ||
1128 | buf += thislen; | ||
1129 | addr += thislen; | ||
1130 | } | ||
956 | 1131 | ||
957 | return 0; | 1132 | return 0; |
958 | } | 1133 | } |
959 | #else | 1134 | #else |
960 | #define onenand_verify_page(...) (0) | 1135 | #define onenand_verify(...) (0) |
961 | #define onenand_verify_oob(...) (0) | 1136 | #define onenand_verify_oob(...) (0) |
962 | #endif | 1137 | #endif |
963 | 1138 | ||
@@ -988,60 +1163,57 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
988 | 1163 | ||
989 | /* Do not allow writes past end of device */ | 1164 | /* Do not allow writes past end of device */ |
990 | if (unlikely((to + len) > mtd->size)) { | 1165 | if (unlikely((to + len) > mtd->size)) { |
991 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n"); | 1166 | printk(KERN_ERR "onenand_write: Attempt write to past end of device\n"); |
992 | return -EINVAL; | 1167 | return -EINVAL; |
993 | } | 1168 | } |
994 | 1169 | ||
995 | /* Reject writes, which are not page aligned */ | 1170 | /* Reject writes, which are not page aligned */ |
996 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { | 1171 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { |
997 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n"); | 1172 | printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n"); |
998 | return -EINVAL; | 1173 | return -EINVAL; |
999 | } | 1174 | } |
1000 | 1175 | ||
1001 | column = to & (mtd->writesize - 1); | 1176 | column = to & (mtd->writesize - 1); |
1002 | subpage = column || (len & (mtd->writesize - 1)); | ||
1003 | 1177 | ||
1004 | /* Grab the lock and see if the device is available */ | 1178 | /* Grab the lock and see if the device is available */ |
1005 | onenand_get_device(mtd, FL_WRITING); | 1179 | onenand_get_device(mtd, FL_WRITING); |
1006 | 1180 | ||
1007 | /* Loop until all data write */ | 1181 | /* Loop until all data write */ |
1008 | while (written < len) { | 1182 | while (written < len) { |
1009 | int bytes = mtd->writesize; | 1183 | 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; | 1184 | u_char *wbuf = (u_char *) buf; |
1012 | 1185 | ||
1013 | cond_resched(); | 1186 | cond_resched(); |
1014 | 1187 | ||
1015 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes); | 1188 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); |
1016 | 1189 | ||
1017 | /* Partial page write */ | 1190 | /* Partial page write */ |
1191 | subpage = thislen < mtd->writesize; | ||
1018 | if (subpage) { | 1192 | if (subpage) { |
1019 | bytes = min_t(int, bytes - column, (int) len); | ||
1020 | memset(this->page_buf, 0xff, mtd->writesize); | 1193 | memset(this->page_buf, 0xff, mtd->writesize); |
1021 | memcpy(this->page_buf + column, buf, bytes); | 1194 | memcpy(this->page_buf + column, buf, thislen); |
1022 | wbuf = this->page_buf; | 1195 | wbuf = this->page_buf; |
1023 | /* Even though partial write, we need page size */ | ||
1024 | thislen = mtd->writesize; | ||
1025 | } | 1196 | } |
1026 | 1197 | ||
1027 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen); | 1198 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); |
1028 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | 1199 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); |
1029 | 1200 | ||
1030 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); | 1201 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); |
1031 | 1202 | ||
1203 | ret = this->wait(mtd, FL_WRITING); | ||
1204 | |||
1032 | /* In partial page write we don't update bufferram */ | 1205 | /* In partial page write we don't update bufferram */ |
1033 | onenand_update_bufferram(mtd, to, !subpage); | 1206 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
1034 | 1207 | ||
1035 | ret = this->wait(mtd, FL_WRITING); | ||
1036 | if (ret) { | 1208 | if (ret) { |
1037 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); | 1209 | printk(KERN_ERR "onenand_write: write filaed %d\n", ret); |
1038 | break; | 1210 | break; |
1039 | } | 1211 | } |
1040 | 1212 | ||
1041 | /* Only check verify write turn on */ | 1213 | /* Only check verify write turn on */ |
1042 | ret = onenand_verify_page(mtd, (u_char *) wbuf, to); | 1214 | ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); |
1043 | if (ret) { | 1215 | if (ret) { |
1044 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); | 1216 | printk(KERN_ERR "onenand_write: verify failed %d\n", ret); |
1045 | break; | 1217 | break; |
1046 | } | 1218 | } |
1047 | 1219 | ||
@@ -1064,20 +1236,58 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1064 | } | 1236 | } |
1065 | 1237 | ||
1066 | /** | 1238 | /** |
1239 | * onenand_fill_auto_oob - [Internal] oob auto-placement transfer | ||
1240 | * @param mtd MTD device structure | ||
1241 | * @param oob_buf oob buffer | ||
1242 | * @param buf source address | ||
1243 | * @param column oob offset to write to | ||
1244 | * @param thislen oob length to write | ||
1245 | */ | ||
1246 | static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, | ||
1247 | const u_char *buf, int column, int thislen) | ||
1248 | { | ||
1249 | struct onenand_chip *this = mtd->priv; | ||
1250 | struct nand_oobfree *free; | ||
1251 | int writecol = column; | ||
1252 | int writeend = column + thislen; | ||
1253 | int lastgap = 0; | ||
1254 | |||
1255 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
1256 | if (writecol >= lastgap) | ||
1257 | writecol += free->offset - lastgap; | ||
1258 | if (writeend >= lastgap) | ||
1259 | writeend += free->offset - lastgap; | ||
1260 | lastgap = free->offset + free->length; | ||
1261 | } | ||
1262 | for (free = this->ecclayout->oobfree; free->length; ++free) { | ||
1263 | int free_end = free->offset + free->length; | ||
1264 | if (free->offset < writeend && free_end > writecol) { | ||
1265 | int st = max_t(int,free->offset,writecol); | ||
1266 | int ed = min_t(int,free_end,writeend); | ||
1267 | int n = ed - st; | ||
1268 | memcpy(oob_buf + st, buf, n); | ||
1269 | buf += n; | ||
1270 | } | ||
1271 | } | ||
1272 | return 0; | ||
1273 | } | ||
1274 | |||
1275 | /** | ||
1067 | * onenand_do_write_oob - [Internal] OneNAND write out-of-band | 1276 | * onenand_do_write_oob - [Internal] OneNAND write out-of-band |
1068 | * @param mtd MTD device structure | 1277 | * @param mtd MTD device structure |
1069 | * @param to offset to write to | 1278 | * @param to offset to write to |
1070 | * @param len number of bytes to write | 1279 | * @param len number of bytes to write |
1071 | * @param retlen pointer to variable to store the number of written bytes | 1280 | * @param retlen pointer to variable to store the number of written bytes |
1072 | * @param buf the data to write | 1281 | * @param buf the data to write |
1282 | * @param mode operation mode | ||
1073 | * | 1283 | * |
1074 | * OneNAND write out-of-band | 1284 | * OneNAND write out-of-band |
1075 | */ | 1285 | */ |
1076 | static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | 1286 | static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, |
1077 | size_t *retlen, const u_char *buf) | 1287 | size_t *retlen, const u_char *buf, mtd_oob_mode_t mode) |
1078 | { | 1288 | { |
1079 | struct onenand_chip *this = mtd->priv; | 1289 | struct onenand_chip *this = mtd->priv; |
1080 | int column, ret = 0; | 1290 | int column, ret = 0, oobsize; |
1081 | int written = 0; | 1291 | int written = 0; |
1082 | 1292 | ||
1083 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | 1293 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); |
@@ -1085,9 +1295,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 */ | 1295 | /* Initialize retlen, in case of early exit */ |
1086 | *retlen = 0; | 1296 | *retlen = 0; |
1087 | 1297 | ||
1088 | /* Do not allow writes past end of device */ | 1298 | if (mode == MTD_OOB_AUTO) |
1089 | if (unlikely((to + len) > mtd->size)) { | 1299 | oobsize = this->ecclayout->oobavail; |
1090 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); | 1300 | else |
1301 | oobsize = mtd->oobsize; | ||
1302 | |||
1303 | column = to & (mtd->oobsize - 1); | ||
1304 | |||
1305 | if (unlikely(column >= oobsize)) { | ||
1306 | printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n"); | ||
1307 | return -EINVAL; | ||
1308 | } | ||
1309 | |||
1310 | /* For compatibility with NAND: Do not allow write past end of page */ | ||
1311 | if (column + len > oobsize) { | ||
1312 | printk(KERN_ERR "onenand_write_oob: " | ||
1313 | "Attempt to write past end of page\n"); | ||
1314 | return -EINVAL; | ||
1315 | } | ||
1316 | |||
1317 | /* Do not allow reads past end of device */ | ||
1318 | if (unlikely(to >= mtd->size || | ||
1319 | column + len > ((mtd->size >> this->page_shift) - | ||
1320 | (to >> this->page_shift)) * oobsize)) { | ||
1321 | printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n"); | ||
1091 | return -EINVAL; | 1322 | return -EINVAL; |
1092 | } | 1323 | } |
1093 | 1324 | ||
@@ -1096,18 +1327,19 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1096 | 1327 | ||
1097 | /* Loop until all data write */ | 1328 | /* Loop until all data write */ |
1098 | while (written < len) { | 1329 | while (written < len) { |
1099 | int thislen = min_t(int, mtd->oobsize, len - written); | 1330 | int thislen = min_t(int, oobsize, len - written); |
1100 | 1331 | ||
1101 | cond_resched(); | 1332 | cond_resched(); |
1102 | 1333 | ||
1103 | column = to & (mtd->oobsize - 1); | ||
1104 | |||
1105 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); | 1334 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); |
1106 | 1335 | ||
1107 | /* We send data to spare ram with oobsize | 1336 | /* We send data to spare ram with oobsize |
1108 | * to prevent byte access */ | 1337 | * to prevent byte access */ |
1109 | memset(this->page_buf, 0xff, mtd->oobsize); | 1338 | memset(this->page_buf, 0xff, mtd->oobsize); |
1110 | memcpy(this->page_buf + column, buf, thislen); | 1339 | if (mode == MTD_OOB_AUTO) |
1340 | onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen); | ||
1341 | else | ||
1342 | memcpy(this->page_buf + column, buf, thislen); | ||
1111 | this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); | 1343 | this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); |
1112 | 1344 | ||
1113 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | 1345 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); |
@@ -1116,26 +1348,25 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1116 | 1348 | ||
1117 | ret = this->wait(mtd, FL_WRITING); | 1349 | ret = this->wait(mtd, FL_WRITING); |
1118 | if (ret) { | 1350 | if (ret) { |
1119 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret); | 1351 | printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret); |
1120 | goto out; | 1352 | break; |
1121 | } | 1353 | } |
1122 | 1354 | ||
1123 | ret = onenand_verify_oob(mtd, buf, to, thislen); | 1355 | ret = onenand_verify_oob(mtd, this->page_buf, to); |
1124 | if (ret) { | 1356 | if (ret) { |
1125 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); | 1357 | printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); |
1126 | goto out; | 1358 | break; |
1127 | } | 1359 | } |
1128 | 1360 | ||
1129 | written += thislen; | 1361 | written += thislen; |
1130 | |||
1131 | if (written == len) | 1362 | if (written == len) |
1132 | break; | 1363 | break; |
1133 | 1364 | ||
1134 | to += thislen; | 1365 | to += mtd->writesize; |
1135 | buf += thislen; | 1366 | buf += thislen; |
1367 | column = 0; | ||
1136 | } | 1368 | } |
1137 | 1369 | ||
1138 | out: | ||
1139 | /* Deselect and wake up anyone waiting on the device */ | 1370 | /* Deselect and wake up anyone waiting on the device */ |
1140 | onenand_release_device(mtd); | 1371 | onenand_release_device(mtd); |
1141 | 1372 | ||
@@ -1153,10 +1384,17 @@ out: | |||
1153 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, | 1384 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, |
1154 | struct mtd_oob_ops *ops) | 1385 | struct mtd_oob_ops *ops) |
1155 | { | 1386 | { |
1156 | BUG_ON(ops->mode != MTD_OOB_PLACE); | 1387 | switch (ops->mode) { |
1157 | 1388 | case MTD_OOB_PLACE: | |
1389 | case MTD_OOB_AUTO: | ||
1390 | break; | ||
1391 | case MTD_OOB_RAW: | ||
1392 | /* Not implemented yet */ | ||
1393 | default: | ||
1394 | return -EINVAL; | ||
1395 | } | ||
1158 | return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, | 1396 | return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, |
1159 | &ops->oobretlen, ops->oobbuf); | 1397 | &ops->oobretlen, ops->oobbuf, ops->mode); |
1160 | } | 1398 | } |
1161 | 1399 | ||
1162 | /** | 1400 | /** |
@@ -1199,19 +1437,19 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1199 | 1437 | ||
1200 | /* Start address must align on block boundary */ | 1438 | /* Start address must align on block boundary */ |
1201 | if (unlikely(instr->addr & (block_size - 1))) { | 1439 | if (unlikely(instr->addr & (block_size - 1))) { |
1202 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); | 1440 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); |
1203 | return -EINVAL; | 1441 | return -EINVAL; |
1204 | } | 1442 | } |
1205 | 1443 | ||
1206 | /* Length must align on block boundary */ | 1444 | /* Length must align on block boundary */ |
1207 | if (unlikely(instr->len & (block_size - 1))) { | 1445 | if (unlikely(instr->len & (block_size - 1))) { |
1208 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); | 1446 | printk(KERN_ERR "onenand_erase: Length not block aligned\n"); |
1209 | return -EINVAL; | 1447 | return -EINVAL; |
1210 | } | 1448 | } |
1211 | 1449 | ||
1212 | /* Do not allow erase past end of device */ | 1450 | /* Do not allow erase past end of device */ |
1213 | if (unlikely((instr->len + instr->addr) > mtd->size)) { | 1451 | if (unlikely((instr->len + instr->addr) > mtd->size)) { |
1214 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); | 1452 | printk(KERN_ERR "onenand_erase: Erase past end of device\n"); |
1215 | return -EINVAL; | 1453 | return -EINVAL; |
1216 | } | 1454 | } |
1217 | 1455 | ||
@@ -1241,7 +1479,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1241 | ret = this->wait(mtd, FL_ERASING); | 1479 | ret = this->wait(mtd, FL_ERASING); |
1242 | /* Check, if it is write protected */ | 1480 | /* Check, if it is write protected */ |
1243 | if (ret) { | 1481 | if (ret) { |
1244 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); | 1482 | printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); |
1245 | instr->state = MTD_ERASE_FAILED; | 1483 | instr->state = MTD_ERASE_FAILED; |
1246 | instr->fail_addr = addr; | 1484 | instr->fail_addr = addr; |
1247 | goto erase_exit; | 1485 | goto erase_exit; |
@@ -1322,7 +1560,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1322 | 1560 | ||
1323 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 1561 | /* We write two bytes, so we dont have to mess with 16 bit access */ |
1324 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | 1562 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); |
1325 | return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); | 1563 | return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE); |
1326 | } | 1564 | } |
1327 | 1565 | ||
1328 | /** | 1566 | /** |
@@ -1491,6 +1729,8 @@ static int onenand_unlock_all(struct mtd_info *mtd) | |||
1491 | struct onenand_chip *this = mtd->priv; | 1729 | struct onenand_chip *this = mtd->priv; |
1492 | 1730 | ||
1493 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { | 1731 | if (this->options & ONENAND_HAS_UNLOCK_ALL) { |
1732 | /* Set start block address */ | ||
1733 | this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | ||
1494 | /* Write unlock command */ | 1734 | /* Write unlock command */ |
1495 | this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); | 1735 | this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); |
1496 | 1736 | ||
@@ -1503,13 +1743,10 @@ static int onenand_unlock_all(struct mtd_info *mtd) | |||
1503 | continue; | 1743 | continue; |
1504 | 1744 | ||
1505 | /* Workaround for all block unlock in DDP */ | 1745 | /* Workaround for all block unlock in DDP */ |
1506 | if (this->device_id & ONENAND_DEVICE_IS_DDP) { | 1746 | if (ONENAND_IS_DDP(this)) { |
1507 | loff_t ofs; | ||
1508 | size_t len; | ||
1509 | |||
1510 | /* 1st block on another chip */ | 1747 | /* 1st block on another chip */ |
1511 | ofs = this->chipsize >> 1; | 1748 | loff_t ofs = this->chipsize >> 1; |
1512 | len = 1 << this->erase_shift; | 1749 | size_t len = mtd->erasesize; |
1513 | 1750 | ||
1514 | onenand_unlock(mtd, ofs, len); | 1751 | onenand_unlock(mtd, ofs, len); |
1515 | } | 1752 | } |
@@ -1617,7 +1854,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); | 1854 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
1618 | this->wait(mtd, FL_OTPING); | 1855 | this->wait(mtd, FL_OTPING); |
1619 | 1856 | ||
1620 | ret = onenand_do_write_oob(mtd, from, len, retlen, buf); | 1857 | ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE); |
1621 | 1858 | ||
1622 | /* Exit OTP access mode */ | 1859 | /* Exit OTP access mode */ |
1623 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 1860 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
@@ -1823,12 +2060,13 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | |||
1823 | #endif /* CONFIG_MTD_ONENAND_OTP */ | 2060 | #endif /* CONFIG_MTD_ONENAND_OTP */ |
1824 | 2061 | ||
1825 | /** | 2062 | /** |
1826 | * onenand_lock_scheme - Check and set OneNAND lock scheme | 2063 | * onenand_check_features - Check and set OneNAND features |
1827 | * @param mtd MTD data structure | 2064 | * @param mtd MTD data structure |
1828 | * | 2065 | * |
1829 | * Check and set OneNAND lock scheme | 2066 | * Check and set OneNAND features |
2067 | * - lock scheme | ||
1830 | */ | 2068 | */ |
1831 | static void onenand_lock_scheme(struct mtd_info *mtd) | 2069 | static void onenand_check_features(struct mtd_info *mtd) |
1832 | { | 2070 | { |
1833 | struct onenand_chip *this = mtd->priv; | 2071 | struct onenand_chip *this = mtd->priv; |
1834 | unsigned int density, process; | 2072 | unsigned int density, process; |
@@ -1961,26 +2199,28 @@ static int onenand_probe(struct mtd_info *mtd) | |||
1961 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; | 2199 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; |
1962 | this->chipsize = (16 << density) << 20; | 2200 | this->chipsize = (16 << density) << 20; |
1963 | /* Set density mask. it is used for DDP */ | 2201 | /* Set density mask. it is used for DDP */ |
1964 | this->density_mask = (1 << (density + 6)); | 2202 | if (ONENAND_IS_DDP(this)) |
2203 | this->density_mask = (1 << (density + 6)); | ||
2204 | else | ||
2205 | this->density_mask = 0; | ||
1965 | 2206 | ||
1966 | /* OneNAND page size & block size */ | 2207 | /* OneNAND page size & block size */ |
1967 | /* The data buffer size is equal to page size */ | 2208 | /* The data buffer size is equal to page size */ |
1968 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); | 2209 | mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); |
1969 | mtd->oobsize = mtd->writesize >> 5; | 2210 | mtd->oobsize = mtd->writesize >> 5; |
1970 | /* Pagers per block is always 64 in OneNAND */ | 2211 | /* Pages per a block are always 64 in OneNAND */ |
1971 | mtd->erasesize = mtd->writesize << 6; | 2212 | mtd->erasesize = mtd->writesize << 6; |
1972 | 2213 | ||
1973 | this->erase_shift = ffs(mtd->erasesize) - 1; | 2214 | this->erase_shift = ffs(mtd->erasesize) - 1; |
1974 | this->page_shift = ffs(mtd->writesize) - 1; | 2215 | this->page_shift = ffs(mtd->writesize) - 1; |
1975 | this->ppb_shift = (this->erase_shift - this->page_shift); | 2216 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; |
1976 | this->page_mask = (mtd->erasesize / mtd->writesize) - 1; | ||
1977 | 2217 | ||
1978 | /* REVIST: Multichip handling */ | 2218 | /* REVIST: Multichip handling */ |
1979 | 2219 | ||
1980 | mtd->size = this->chipsize; | 2220 | mtd->size = this->chipsize; |
1981 | 2221 | ||
1982 | /* Check OneNAND lock scheme */ | 2222 | /* Check OneNAND features */ |
1983 | onenand_lock_scheme(mtd); | 2223 | onenand_check_features(mtd); |
1984 | 2224 | ||
1985 | return 0; | 2225 | return 0; |
1986 | } | 2226 | } |
@@ -2021,6 +2261,7 @@ static void onenand_resume(struct mtd_info *mtd) | |||
2021 | */ | 2261 | */ |
2022 | int onenand_scan(struct mtd_info *mtd, int maxchips) | 2262 | int onenand_scan(struct mtd_info *mtd, int maxchips) |
2023 | { | 2263 | { |
2264 | int i; | ||
2024 | struct onenand_chip *this = mtd->priv; | 2265 | struct onenand_chip *this = mtd->priv; |
2025 | 2266 | ||
2026 | if (!this->read_word) | 2267 | if (!this->read_word) |
@@ -2092,6 +2333,16 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
2092 | } | 2333 | } |
2093 | 2334 | ||
2094 | this->subpagesize = mtd->writesize >> mtd->subpage_sft; | 2335 | this->subpagesize = mtd->writesize >> mtd->subpage_sft; |
2336 | |||
2337 | /* | ||
2338 | * The number of bytes available for a client to place data into | ||
2339 | * the out of band area | ||
2340 | */ | ||
2341 | this->ecclayout->oobavail = 0; | ||
2342 | for (i = 0; this->ecclayout->oobfree[i].length; i++) | ||
2343 | this->ecclayout->oobavail += | ||
2344 | this->ecclayout->oobfree[i].length; | ||
2345 | |||
2095 | mtd->ecclayout = this->ecclayout; | 2346 | mtd->ecclayout = this->ecclayout; |
2096 | 2347 | ||
2097 | /* Fill in remaining MTD driver data */ | 2348 | /* Fill in remaining MTD driver data */ |
@@ -2144,8 +2395,11 @@ void onenand_release(struct mtd_info *mtd) | |||
2144 | del_mtd_device (mtd); | 2395 | del_mtd_device (mtd); |
2145 | 2396 | ||
2146 | /* Free bad block table memory, if allocated */ | 2397 | /* Free bad block table memory, if allocated */ |
2147 | if (this->bbm) | 2398 | if (this->bbm) { |
2399 | struct bbm_info *bbm = this->bbm; | ||
2400 | kfree(bbm->bbt); | ||
2148 | kfree(this->bbm); | 2401 | kfree(this->bbm); |
2402 | } | ||
2149 | /* Buffer allocated by onenand_scan */ | 2403 | /* Buffer allocated by onenand_scan */ |
2150 | if (this->options & ONENAND_PAGEBUF_ALLOC) | 2404 | if (this->options & ONENAND_PAGEBUF_ALLOC) |
2151 | kfree(this->page_buf); | 2405 | kfree(this->page_buf); |
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 98f8fd1c6375..aecdd50a1781 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c | |||
@@ -17,8 +17,8 @@ | |||
17 | #include <linux/mtd/onenand.h> | 17 | #include <linux/mtd/onenand.h> |
18 | #include <linux/mtd/compatmac.h> | 18 | #include <linux/mtd/compatmac.h> |
19 | 19 | ||
20 | extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 20 | extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, |
21 | size_t *retlen, u_char *buf); | 21 | struct mtd_oob_ops *ops); |
22 | 22 | ||
23 | /** | 23 | /** |
24 | * check_short_pattern - [GENERIC] check if a pattern is in the buffer | 24 | * check_short_pattern - [GENERIC] check if a pattern is in the buffer |
@@ -65,10 +65,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
65 | int startblock; | 65 | int startblock; |
66 | loff_t from; | 66 | loff_t from; |
67 | size_t readlen, ooblen; | 67 | size_t readlen, ooblen; |
68 | struct mtd_oob_ops ops; | ||
68 | 69 | ||
69 | printk(KERN_INFO "Scanning device for bad blocks\n"); | 70 | printk(KERN_INFO "Scanning device for bad blocks\n"); |
70 | 71 | ||
71 | len = 1; | 72 | len = 2; |
72 | 73 | ||
73 | /* We need only read few bytes from the OOB area */ | 74 | /* We need only read few bytes from the OOB area */ |
74 | scanlen = ooblen = 0; | 75 | scanlen = ooblen = 0; |
@@ -82,22 +83,24 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
82 | startblock = 0; | 83 | startblock = 0; |
83 | from = 0; | 84 | from = 0; |
84 | 85 | ||
86 | ops.mode = MTD_OOB_PLACE; | ||
87 | ops.ooblen = readlen; | ||
88 | ops.oobbuf = buf; | ||
89 | ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; | ||
90 | |||
85 | for (i = startblock; i < numblocks; ) { | 91 | for (i = startblock; i < numblocks; ) { |
86 | int ret; | 92 | int ret; |
87 | 93 | ||
88 | for (j = 0; j < len; j++) { | 94 | for (j = 0; j < len; j++) { |
89 | size_t retlen; | ||
90 | |||
91 | /* No need to read pages fully, | 95 | /* No need to read pages fully, |
92 | * just read required OOB bytes */ | 96 | * just read required OOB bytes */ |
93 | ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, | 97 | ret = onenand_bbt_read_oob(mtd, from + j * mtd->writesize + bd->offs, &ops); |
94 | readlen, &retlen, &buf[0]); | ||
95 | 98 | ||
96 | /* If it is a initial bad block, just ignore it */ | 99 | /* If it is a initial bad block, just ignore it */ |
97 | if (ret && !(ret & ONENAND_CTRL_LOAD)) | 100 | if (ret == ONENAND_BBT_READ_FATAL_ERROR) |
98 | return ret; | 101 | return -EIO; |
99 | 102 | ||
100 | if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { | 103 | if (ret || check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { |
101 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); | 104 | bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); |
102 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | 105 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", |
103 | i >> 1, (unsigned int) from); | 106 | i >> 1, (unsigned int) from); |
@@ -168,8 +171,8 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) | |||
168 | * marked good / bad blocks and writes the bad block table(s) to | 171 | * marked good / bad blocks and writes the bad block table(s) to |
169 | * the selected place. | 172 | * the selected place. |
170 | * | 173 | * |
171 | * The bad block table memory is allocated here. It must be freed | 174 | * The bad block table memory is allocated here. It is freed |
172 | * by calling the onenand_free_bbt function. | 175 | * by the onenand_release function. |
173 | * | 176 | * |
174 | */ | 177 | */ |
175 | int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) | 178 | int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) |
diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 1221b7c44158..fff8c53e5434 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h | |||
@@ -92,6 +92,13 @@ struct nand_bbt_descr { | |||
92 | */ | 92 | */ |
93 | #define ONENAND_BADBLOCK_POS 0 | 93 | #define ONENAND_BADBLOCK_POS 0 |
94 | 94 | ||
95 | /* | ||
96 | * Bad block scanning errors | ||
97 | */ | ||
98 | #define ONENAND_BBT_READ_ERROR 1 | ||
99 | #define ONENAND_BBT_READ_ECC_ERROR 2 | ||
100 | #define ONENAND_BBT_READ_FATAL_ERROR 4 | ||
101 | |||
95 | /** | 102 | /** |
96 | * struct bbm_info - [GENERIC] Bad Block Table data structure | 103 | * struct bbm_info - [GENERIC] Bad Block Table data structure |
97 | * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry | 104 | * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry |
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index f775a7af3890..d8af8a95e58d 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * linux/include/linux/mtd/onenand.h | 2 | * linux/include/linux/mtd/onenand.h |
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 |
@@ -42,14 +42,10 @@ typedef enum { | |||
42 | 42 | ||
43 | /** | 43 | /** |
44 | * struct onenand_bufferram - OneNAND BufferRAM Data | 44 | * struct onenand_bufferram - OneNAND BufferRAM Data |
45 | * @block: block address in BufferRAM | 45 | * @blockpage: block & page address in BufferRAM |
46 | * @page: page address in BufferRAM | ||
47 | * @valid: valid flag | ||
48 | */ | 46 | */ |
49 | struct onenand_bufferram { | 47 | struct onenand_bufferram { |
50 | int block; | 48 | int blockpage; |
51 | int page; | ||
52 | int valid; | ||
53 | }; | 49 | }; |
54 | 50 | ||
55 | /** | 51 | /** |
@@ -63,7 +59,6 @@ struct onenand_bufferram { | |||
63 | * partly be set to inform onenand_scan about | 59 | * partly be set to inform onenand_scan about |
64 | * @erase_shift: [INTERN] number of address bits in a block | 60 | * @erase_shift: [INTERN] number of address bits in a block |
65 | * @page_shift: [INTERN] number of address bits in a page | 61 | * @page_shift: [INTERN] number of address bits in a page |
66 | * @ppb_shift: [INTERN] number of address bits in a pages per block | ||
67 | * @page_mask: [INTERN] a page per block mask | 62 | * @page_mask: [INTERN] a page per block mask |
68 | * @bufferram_index: [INTERN] BufferRAM index | 63 | * @bufferram_index: [INTERN] BufferRAM index |
69 | * @bufferram: [INTERN] BufferRAM info | 64 | * @bufferram: [INTERN] BufferRAM info |
@@ -103,7 +98,6 @@ struct onenand_chip { | |||
103 | 98 | ||
104 | unsigned int erase_shift; | 99 | unsigned int erase_shift; |
105 | unsigned int page_shift; | 100 | unsigned int page_shift; |
106 | unsigned int ppb_shift; /* Pages per block shift */ | ||
107 | unsigned int page_mask; | 101 | unsigned int page_mask; |
108 | 102 | ||
109 | unsigned int bufferram_index; | 103 | unsigned int bufferram_index; |
@@ -150,6 +144,9 @@ struct onenand_chip { | |||
150 | #define ONENAND_SET_SYS_CFG1(v, this) \ | 144 | #define ONENAND_SET_SYS_CFG1(v, this) \ |
151 | (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1)) | 145 | (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1)) |
152 | 146 | ||
147 | #define ONENAND_IS_DDP(this) \ | ||
148 | (this->device_id & ONENAND_DEVICE_IS_DDP) | ||
149 | |||
153 | /* Check byte access in OneNAND */ | 150 | /* Check byte access in OneNAND */ |
154 | #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) | 151 | #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) |
155 | 152 | ||
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index e31c8f5d4271..af94719890e7 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h | |||
@@ -3,7 +3,8 @@ | |||
3 | * | 3 | * |
4 | * OneNAND Register header file | 4 | * OneNAND Register header file |
5 | * | 5 | * |
6 | * Copyright (C) 2005-2006 Samsung Electronics | 6 | * Copyright (C) 2005-2007 Samsung Electronics |
7 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
7 | * | 8 | * |
8 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
@@ -80,9 +81,11 @@ | |||
80 | #define ONENAND_VERSION_PROCESS_SHIFT (8) | 81 | #define ONENAND_VERSION_PROCESS_SHIFT (8) |
81 | 82 | ||
82 | /* | 83 | /* |
83 | * Start Address 1 F100h (R/W) | 84 | * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W) |
84 | */ | 85 | */ |
85 | #define ONENAND_DDP_SHIFT (15) | 86 | #define ONENAND_DDP_SHIFT (15) |
87 | #define ONENAND_DDP_CHIP0 (0) | ||
88 | #define ONENAND_DDP_CHIP1 (1 << ONENAND_DDP_SHIFT) | ||
86 | 89 | ||
87 | /* | 90 | /* |
88 | * Start Address 8 F107h (R/W) | 91 | * Start Address 8 F107h (R/W) |