diff options
-rw-r--r-- | drivers/mtd/devices/doc2000.c | 39 | ||||
-rw-r--r-- | drivers/mtd/devices/doc2001.c | 34 | ||||
-rw-r--r-- | drivers/mtd/devices/doc2001plus.c | 34 | ||||
-rw-r--r-- | drivers/mtd/inftlcore.c | 111 | ||||
-rw-r--r-- | drivers/mtd/inftlmount.c | 27 | ||||
-rw-r--r-- | drivers/mtd/mtdchar.c | 59 | ||||
-rw-r--r-- | drivers/mtd/mtdconcat.c | 90 | ||||
-rw-r--r-- | drivers/mtd/mtdpart.c | 29 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 542 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 188 | ||||
-rw-r--r-- | drivers/mtd/nftlcore.c | 92 | ||||
-rw-r--r-- | drivers/mtd/nftlmount.c | 29 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 46 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 7 | ||||
-rw-r--r-- | fs/jffs2/jffs2_fs_sb.h | 1 | ||||
-rw-r--r-- | fs/jffs2/wbuf.c | 230 | ||||
-rw-r--r-- | include/linux/mtd/mtd.h | 50 | ||||
-rw-r--r-- | include/linux/mtd/nand.h | 10 |
18 files changed, 1028 insertions, 590 deletions
diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index d9ba1ee658f6..c54e40464d82 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c | |||
@@ -59,10 +59,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
59 | size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); | 59 | size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); |
60 | static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, | 60 | static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, |
61 | size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); | 61 | size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); |
62 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 62 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, |
63 | size_t *retlen, u_char *buf); | 63 | struct mtd_oob_ops *ops); |
64 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 64 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, |
65 | size_t *retlen, const u_char *buf); | 65 | struct mtd_oob_ops *ops); |
66 | static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, | 66 | static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, |
67 | size_t *retlen, const u_char *buf); | 67 | size_t *retlen, const u_char *buf); |
68 | static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); | 68 | static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); |
@@ -959,12 +959,18 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, | |||
959 | return 0; | 959 | return 0; |
960 | } | 960 | } |
961 | 961 | ||
962 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 962 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, |
963 | size_t * retlen, u_char * buf) | 963 | struct mtd_oob_ops *ops) |
964 | { | 964 | { |
965 | struct DiskOnChip *this = mtd->priv; | 965 | struct DiskOnChip *this = mtd->priv; |
966 | int len256 = 0, ret; | 966 | int len256 = 0, ret; |
967 | struct Nand *mychip; | 967 | struct Nand *mychip; |
968 | uint8_t *buf = ops->oobbuf; | ||
969 | size_t len = ops->len; | ||
970 | |||
971 | BUG_ON(ops->mode != MTD_OOB_PLACE); | ||
972 | |||
973 | ofs += ops->ooboffs; | ||
968 | 974 | ||
969 | mutex_lock(&this->lock); | 975 | mutex_lock(&this->lock); |
970 | 976 | ||
@@ -1005,7 +1011,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
1005 | 1011 | ||
1006 | DoC_ReadBuf(this, &buf[len256], len - len256); | 1012 | DoC_ReadBuf(this, &buf[len256], len - len256); |
1007 | 1013 | ||
1008 | *retlen = len; | 1014 | ops->retlen = len; |
1009 | /* Reading the full OOB data drops us off of the end of the page, | 1015 | /* Reading the full OOB data drops us off of the end of the page, |
1010 | * causing the flash device to go into busy mode, so we need | 1016 | * causing the flash device to go into busy mode, so we need |
1011 | * to wait until ready 11.4.1 and Toshiba TC58256FT docs */ | 1017 | * to wait until ready 11.4.1 and Toshiba TC58256FT docs */ |
@@ -1120,17 +1126,20 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
1120 | 1126 | ||
1121 | } | 1127 | } |
1122 | 1128 | ||
1123 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 1129 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, |
1124 | size_t * retlen, const u_char * buf) | 1130 | struct mtd_oob_ops *ops) |
1125 | { | 1131 | { |
1126 | struct DiskOnChip *this = mtd->priv; | 1132 | struct DiskOnChip *this = mtd->priv; |
1127 | int ret; | 1133 | int ret; |
1128 | 1134 | ||
1129 | mutex_lock(&this->lock); | 1135 | BUG_ON(ops->mode != MTD_OOB_PLACE); |
1130 | ret = doc_write_oob_nolock(mtd, ofs, len, retlen, buf); | 1136 | |
1137 | mutex_lock(&this->lock); | ||
1138 | ret = doc_write_oob_nolock(mtd, ofs + ops->ooboffs, ops->len, | ||
1139 | &ops->retlen, ops->oobbuf); | ||
1131 | 1140 | ||
1132 | mutex_unlock(&this->lock); | 1141 | mutex_unlock(&this->lock); |
1133 | return ret; | 1142 | return ret; |
1134 | } | 1143 | } |
1135 | 1144 | ||
1136 | static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) | 1145 | static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) |
diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 579c0b570ae5..0cf022a69e65 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c | |||
@@ -43,10 +43,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
43 | static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, | 43 | static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, |
44 | size_t *retlen, const u_char *buf, u_char *eccbuf, | 44 | size_t *retlen, const u_char *buf, u_char *eccbuf, |
45 | struct nand_oobinfo *oobsel); | 45 | struct nand_oobinfo *oobsel); |
46 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 46 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, |
47 | size_t *retlen, u_char *buf); | 47 | struct mtd_oob_ops *ops); |
48 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 48 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, |
49 | size_t *retlen, const u_char *buf); | 49 | struct mtd_oob_ops *ops); |
50 | static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); | 50 | static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); |
51 | 51 | ||
52 | static struct mtd_info *docmillist = NULL; | 52 | static struct mtd_info *docmillist = NULL; |
@@ -662,8 +662,8 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, | |||
662 | return ret; | 662 | return ret; |
663 | } | 663 | } |
664 | 664 | ||
665 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 665 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, |
666 | size_t *retlen, u_char *buf) | 666 | struct mtd_oob_ops *ops) |
667 | { | 667 | { |
668 | #ifndef USE_MEMCPY | 668 | #ifndef USE_MEMCPY |
669 | int i; | 669 | int i; |
@@ -672,6 +672,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
672 | struct DiskOnChip *this = mtd->priv; | 672 | struct DiskOnChip *this = mtd->priv; |
673 | void __iomem *docptr = this->virtadr; | 673 | void __iomem *docptr = this->virtadr; |
674 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; | 674 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; |
675 | uint8_t *buf = ops->oobbuf; | ||
676 | size_t len = ops->len; | ||
677 | |||
678 | BUG_ON(ops->mode != MTD_OOB_PLACE); | ||
679 | |||
680 | ofs += ops->ooboffs; | ||
675 | 681 | ||
676 | /* Find the chip which is to be used and select it */ | 682 | /* Find the chip which is to be used and select it */ |
677 | if (this->curfloor != mychip->floor) { | 683 | if (this->curfloor != mychip->floor) { |
@@ -708,13 +714,13 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
708 | #endif | 714 | #endif |
709 | buf[len - 1] = ReadDOC(docptr, LastDataRead); | 715 | buf[len - 1] = ReadDOC(docptr, LastDataRead); |
710 | 716 | ||
711 | *retlen = len; | 717 | ops->retlen = len; |
712 | 718 | ||
713 | return 0; | 719 | return 0; |
714 | } | 720 | } |
715 | 721 | ||
716 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 722 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, |
717 | size_t *retlen, const u_char *buf) | 723 | struct mtd_oob_ops *ops) |
718 | { | 724 | { |
719 | #ifndef USE_MEMCPY | 725 | #ifndef USE_MEMCPY |
720 | int i; | 726 | int i; |
@@ -724,6 +730,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
724 | struct DiskOnChip *this = mtd->priv; | 730 | struct DiskOnChip *this = mtd->priv; |
725 | void __iomem *docptr = this->virtadr; | 731 | void __iomem *docptr = this->virtadr; |
726 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; | 732 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; |
733 | uint8_t *buf = ops->oobbuf; | ||
734 | size_t len = ops->len; | ||
735 | |||
736 | BUG_ON(ops->mode != MTD_OOB_PLACE); | ||
737 | |||
738 | ofs += ops->ooboffs; | ||
727 | 739 | ||
728 | /* Find the chip which is to be used and select it */ | 740 | /* Find the chip which is to be used and select it */ |
729 | if (this->curfloor != mychip->floor) { | 741 | if (this->curfloor != mychip->floor) { |
@@ -775,12 +787,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
775 | if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { | 787 | if (ReadDOC(docptr, Mil_CDSN_IO) & 1) { |
776 | printk("Error programming oob data\n"); | 788 | printk("Error programming oob data\n"); |
777 | /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ | 789 | /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ |
778 | *retlen = 0; | 790 | ops->retlen = 0; |
779 | ret = -EIO; | 791 | ret = -EIO; |
780 | } | 792 | } |
781 | dummy = ReadDOC(docptr, LastDataRead); | 793 | dummy = ReadDOC(docptr, LastDataRead); |
782 | 794 | ||
783 | *retlen = len; | 795 | ops->retlen = len; |
784 | 796 | ||
785 | return ret; | 797 | return ret; |
786 | } | 798 | } |
diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 1ee0c0dcb53b..66cb1e50469a 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c | |||
@@ -47,10 +47,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | |||
47 | static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, | 47 | static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, |
48 | size_t *retlen, const u_char *buf, u_char *eccbuf, | 48 | size_t *retlen, const u_char *buf, u_char *eccbuf, |
49 | struct nand_oobinfo *oobsel); | 49 | struct nand_oobinfo *oobsel); |
50 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 50 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, |
51 | size_t *retlen, u_char *buf); | 51 | struct mtd_oob_ops *ops); |
52 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 52 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, |
53 | size_t *retlen, const u_char *buf); | 53 | struct mtd_oob_ops *ops); |
54 | static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); | 54 | static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); |
55 | 55 | ||
56 | static struct mtd_info *docmilpluslist = NULL; | 56 | static struct mtd_info *docmilpluslist = NULL; |
@@ -868,14 +868,20 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, | |||
868 | return ret; | 868 | return ret; |
869 | } | 869 | } |
870 | 870 | ||
871 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 871 | static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, |
872 | size_t *retlen, u_char *buf) | 872 | struct mtd_oob_ops *ops) |
873 | { | 873 | { |
874 | loff_t fofs, base; | 874 | loff_t fofs, base; |
875 | struct DiskOnChip *this = mtd->priv; | 875 | struct DiskOnChip *this = mtd->priv; |
876 | void __iomem * docptr = this->virtadr; | 876 | void __iomem * docptr = this->virtadr; |
877 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; | 877 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; |
878 | size_t i, size, got, want; | 878 | size_t i, size, got, want; |
879 | uint8_t *buf = ops->oobbuf; | ||
880 | size_t len = ops->len; | ||
881 | |||
882 | BUG_ON(ops->mode != MTD_OOB_PLACE); | ||
883 | |||
884 | ofs += ops->ooboffs; | ||
879 | 885 | ||
880 | DoC_CheckASIC(docptr); | 886 | DoC_CheckASIC(docptr); |
881 | 887 | ||
@@ -941,12 +947,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
941 | /* Disable flash internally */ | 947 | /* Disable flash internally */ |
942 | WriteDOC(0, docptr, Mplus_FlashSelect); | 948 | WriteDOC(0, docptr, Mplus_FlashSelect); |
943 | 949 | ||
944 | *retlen = len; | 950 | ops->retlen = len; |
945 | return 0; | 951 | return 0; |
946 | } | 952 | } |
947 | 953 | ||
948 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | 954 | static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, |
949 | size_t *retlen, const u_char *buf) | 955 | struct mtd_oob_ops *ops) |
950 | { | 956 | { |
951 | volatile char dummy; | 957 | volatile char dummy; |
952 | loff_t fofs, base; | 958 | loff_t fofs, base; |
@@ -955,6 +961,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
955 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; | 961 | struct Nand *mychip = &this->chips[ofs >> this->chipshift]; |
956 | size_t i, size, got, want; | 962 | size_t i, size, got, want; |
957 | int ret = 0; | 963 | int ret = 0; |
964 | uint8_t *buf = ops->oobbuf; | ||
965 | size_t len = ops->len; | ||
966 | |||
967 | BUG_ON(ops->mode != MTD_OOB_PLACE); | ||
968 | |||
969 | ofs += ops->ooboffs; | ||
958 | 970 | ||
959 | DoC_CheckASIC(docptr); | 971 | DoC_CheckASIC(docptr); |
960 | 972 | ||
@@ -1030,7 +1042,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
1030 | printk("MTD: Error 0x%x programming oob at 0x%x\n", | 1042 | printk("MTD: Error 0x%x programming oob at 0x%x\n", |
1031 | dummy, (int)ofs); | 1043 | dummy, (int)ofs); |
1032 | /* FIXME: implement Bad Block Replacement */ | 1044 | /* FIXME: implement Bad Block Replacement */ |
1033 | *retlen = 0; | 1045 | ops->retlen = 0; |
1034 | ret = -EIO; | 1046 | ret = -EIO; |
1035 | } | 1047 | } |
1036 | dummy = ReadDOC(docptr, Mplus_LastDataRead); | 1048 | dummy = ReadDOC(docptr, Mplus_LastDataRead); |
@@ -1043,7 +1055,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, | |||
1043 | /* Disable flash internally */ | 1055 | /* Disable flash internally */ |
1044 | WriteDOC(0, docptr, Mplus_FlashSelect); | 1056 | WriteDOC(0, docptr, Mplus_FlashSelect); |
1045 | 1057 | ||
1046 | *retlen = len; | 1058 | ops->retlen = len; |
1047 | return ret; | 1059 | return ret; |
1048 | } | 1060 | } |
1049 | 1061 | ||
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 3396f0e1ac5f..efb1a95aa0a0 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c | |||
@@ -151,6 +151,69 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev) | |||
151 | */ | 151 | */ |
152 | 152 | ||
153 | /* | 153 | /* |
154 | * Read oob data from flash | ||
155 | */ | ||
156 | int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, | ||
157 | size_t *retlen, uint8_t *buf) | ||
158 | { | ||
159 | struct mtd_oob_ops ops; | ||
160 | int res; | ||
161 | |||
162 | ops.mode = MTD_OOB_PLACE; | ||
163 | ops.ooboffs = offs & (mtd->writesize - 1); | ||
164 | ops.ooblen = len; | ||
165 | ops.oobbuf = buf; | ||
166 | ops.datbuf = NULL; | ||
167 | ops.len = len; | ||
168 | |||
169 | res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); | ||
170 | *retlen = ops.retlen; | ||
171 | return res; | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * Write oob data to flash | ||
176 | */ | ||
177 | int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, | ||
178 | size_t *retlen, uint8_t *buf) | ||
179 | { | ||
180 | struct mtd_oob_ops ops; | ||
181 | int res; | ||
182 | |||
183 | ops.mode = MTD_OOB_PLACE; | ||
184 | ops.ooboffs = offs & (mtd->writesize - 1); | ||
185 | ops.ooblen = len; | ||
186 | ops.oobbuf = buf; | ||
187 | ops.datbuf = NULL; | ||
188 | ops.len = len; | ||
189 | |||
190 | res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); | ||
191 | *retlen = ops.retlen; | ||
192 | return res; | ||
193 | } | ||
194 | |||
195 | /* | ||
196 | * Write data and oob to flash | ||
197 | */ | ||
198 | static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len, | ||
199 | size_t *retlen, uint8_t *buf, uint8_t *oob) | ||
200 | { | ||
201 | struct mtd_oob_ops ops; | ||
202 | int res; | ||
203 | |||
204 | ops.mode = MTD_OOB_PLACE; | ||
205 | ops.ooboffs = offs; | ||
206 | ops.ooblen = mtd->oobsize; | ||
207 | ops.oobbuf = oob; | ||
208 | ops.datbuf = buf; | ||
209 | ops.len = len; | ||
210 | |||
211 | res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); | ||
212 | *retlen = ops.retlen; | ||
213 | return res; | ||
214 | } | ||
215 | |||
216 | /* | ||
154 | * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. | 217 | * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. |
155 | * This function is used when the give Virtual Unit Chain. | 218 | * This function is used when the give Virtual Unit Chain. |
156 | */ | 219 | */ |
@@ -227,9 +290,9 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned | |||
227 | if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) | 290 | if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) |
228 | continue; | 291 | continue; |
229 | 292 | ||
230 | if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) | 293 | if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) |
231 | + (block * SECTORSIZE), 16 , &retlen, | 294 | + (block * SECTORSIZE), 16, &retlen, |
232 | (char *)&oob) < 0) | 295 | (char *)&oob) < 0) |
233 | status = SECTOR_IGNORE; | 296 | status = SECTOR_IGNORE; |
234 | else | 297 | else |
235 | status = oob.b.Status | oob.b.Status1; | 298 | status = oob.b.Status | oob.b.Status1; |
@@ -304,9 +367,9 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned | |||
304 | memset(&oob, 0xff, sizeof(struct inftl_oob)); | 367 | memset(&oob, 0xff, sizeof(struct inftl_oob)); |
305 | oob.b.Status = oob.b.Status1 = SECTOR_USED; | 368 | oob.b.Status = oob.b.Status1 = SECTOR_USED; |
306 | 369 | ||
307 | nand_write_raw(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + | 370 | inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + |
308 | (block * SECTORSIZE), SECTORSIZE, &retlen, | 371 | (block * SECTORSIZE), SECTORSIZE, &retlen, |
309 | movebuf, (char *)&oob); | 372 | movebuf, (char *)&oob); |
310 | } | 373 | } |
311 | 374 | ||
312 | /* | 375 | /* |
@@ -437,8 +500,8 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) | |||
437 | silly = MAX_LOOPS; | 500 | silly = MAX_LOOPS; |
438 | 501 | ||
439 | while (thisEUN <= inftl->lastEUN) { | 502 | while (thisEUN <= inftl->lastEUN) { |
440 | mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + | 503 | inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + |
441 | blockofs, 8, &retlen, (char *)&bci); | 504 | blockofs, 8, &retlen, (char *)&bci); |
442 | 505 | ||
443 | status = bci.Status | bci.Status1; | 506 | status = bci.Status | bci.Status1; |
444 | DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " | 507 | DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " |
@@ -525,8 +588,8 @@ hitused: | |||
525 | nacs = 0; | 588 | nacs = 0; |
526 | thisEUN = inftl->VUtable[thisVUC]; | 589 | thisEUN = inftl->VUtable[thisVUC]; |
527 | if (thisEUN != BLOCK_NIL) { | 590 | if (thisEUN != BLOCK_NIL) { |
528 | mtd->read_oob(mtd, thisEUN * inftl->EraseSize | 591 | inftl_read_oob(mtd, thisEUN * inftl->EraseSize |
529 | + 8, 8, &retlen, (char *)&oob.u); | 592 | + 8, 8, &retlen, (char *)&oob.u); |
530 | anac = oob.u.a.ANAC + 1; | 593 | anac = oob.u.a.ANAC + 1; |
531 | nacs = oob.u.a.NACs + 1; | 594 | nacs = oob.u.a.NACs + 1; |
532 | } | 595 | } |
@@ -547,8 +610,8 @@ hitused: | |||
547 | oob.u.a.parityPerField = parity; | 610 | oob.u.a.parityPerField = parity; |
548 | oob.u.a.discarded = 0xaa; | 611 | oob.u.a.discarded = 0xaa; |
549 | 612 | ||
550 | mtd->write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8, | 613 | inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8, |
551 | &retlen, (char *)&oob.u); | 614 | &retlen, (char *)&oob.u); |
552 | 615 | ||
553 | /* Also back up header... */ | 616 | /* Also back up header... */ |
554 | oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); | 617 | oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); |
@@ -558,8 +621,8 @@ hitused: | |||
558 | oob.u.b.parityPerField = parity; | 621 | oob.u.b.parityPerField = parity; |
559 | oob.u.b.discarded = 0xaa; | 622 | oob.u.b.discarded = 0xaa; |
560 | 623 | ||
561 | mtd->write_oob(mtd, writeEUN * inftl->EraseSize + | 624 | inftl_write_oob(mtd, writeEUN * inftl->EraseSize + |
562 | SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); | 625 | SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); |
563 | 626 | ||
564 | inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; | 627 | inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; |
565 | inftl->VUtable[thisVUC] = writeEUN; | 628 | inftl->VUtable[thisVUC] = writeEUN; |
@@ -610,8 +673,8 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) | |||
610 | if (BlockUsed[block] || BlockDeleted[block]) | 673 | if (BlockUsed[block] || BlockDeleted[block]) |
611 | continue; | 674 | continue; |
612 | 675 | ||
613 | if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) | 676 | if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) |
614 | + (block * SECTORSIZE), 8 , &retlen, | 677 | + (block * SECTORSIZE), 8 , &retlen, |
615 | (char *)&bci) < 0) | 678 | (char *)&bci) < 0) |
616 | status = SECTOR_IGNORE; | 679 | status = SECTOR_IGNORE; |
617 | else | 680 | else |
@@ -711,8 +774,8 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) | |||
711 | "block=%d)\n", inftl, block); | 774 | "block=%d)\n", inftl, block); |
712 | 775 | ||
713 | while (thisEUN < inftl->nb_blocks) { | 776 | while (thisEUN < inftl->nb_blocks) { |
714 | if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + | 777 | if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + |
715 | blockofs, 8, &retlen, (char *)&bci) < 0) | 778 | blockofs, 8, &retlen, (char *)&bci) < 0) |
716 | status = SECTOR_IGNORE; | 779 | status = SECTOR_IGNORE; |
717 | else | 780 | else |
718 | status = bci.Status | bci.Status1; | 781 | status = bci.Status | bci.Status1; |
@@ -746,10 +809,10 @@ foundit: | |||
746 | if (thisEUN != BLOCK_NIL) { | 809 | if (thisEUN != BLOCK_NIL) { |
747 | loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; | 810 | loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; |
748 | 811 | ||
749 | if (mtd->read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) | 812 | if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) |
750 | return -EIO; | 813 | return -EIO; |
751 | bci.Status = bci.Status1 = SECTOR_DELETED; | 814 | bci.Status = bci.Status1 = SECTOR_DELETED; |
752 | if (mtd->write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) | 815 | if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0) |
753 | return -EIO; | 816 | return -EIO; |
754 | INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); | 817 | INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); |
755 | } | 818 | } |
@@ -790,9 +853,9 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, | |||
790 | memset(&oob, 0xff, sizeof(struct inftl_oob)); | 853 | memset(&oob, 0xff, sizeof(struct inftl_oob)); |
791 | oob.b.Status = oob.b.Status1 = SECTOR_USED; | 854 | oob.b.Status = oob.b.Status1 = SECTOR_USED; |
792 | 855 | ||
793 | nand_write_raw(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + | 856 | inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + |
794 | blockofs, SECTORSIZE, &retlen, (char *)buffer, | 857 | blockofs, SECTORSIZE, &retlen, (char *)buffer, |
795 | (char *)&oob); | 858 | (char *)&oob); |
796 | /* | 859 | /* |
797 | * need to write SECTOR_USED flags since they are not written | 860 | * need to write SECTOR_USED flags since they are not written |
798 | * in mtd_writeecc | 861 | * in mtd_writeecc |
@@ -820,7 +883,7 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, | |||
820 | "buffer=%p)\n", inftl, block, buffer); | 883 | "buffer=%p)\n", inftl, block, buffer); |
821 | 884 | ||
822 | while (thisEUN < inftl->nb_blocks) { | 885 | while (thisEUN < inftl->nb_blocks) { |
823 | if (mtd->read_oob(mtd, (thisEUN * inftl->EraseSize) + | 886 | if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) + |
824 | blockofs, 8, &retlen, (char *)&bci) < 0) | 887 | blockofs, 8, &retlen, (char *)&bci) < 0) |
825 | status = SECTOR_IGNORE; | 888 | status = SECTOR_IGNORE; |
826 | else | 889 | else |
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index b4cda7d0a52d..8f6006f1a519 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c | |||
@@ -43,6 +43,11 @@ | |||
43 | 43 | ||
44 | char inftlmountrev[]="$Revision: 1.18 $"; | 44 | char inftlmountrev[]="$Revision: 1.18 $"; |
45 | 45 | ||
46 | extern int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, | ||
47 | size_t *retlen, uint8_t *buf); | ||
48 | extern int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, | ||
49 | size_t *retlen, uint8_t *buf); | ||
50 | |||
46 | /* | 51 | /* |
47 | * find_boot_record: Find the INFTL Media Header and its Spare copy which | 52 | * find_boot_record: Find the INFTL Media Header and its Spare copy which |
48 | * contains the various device information of the INFTL partition and | 53 | * contains the various device information of the INFTL partition and |
@@ -107,9 +112,9 @@ static int find_boot_record(struct INFTLrecord *inftl) | |||
107 | } | 112 | } |
108 | 113 | ||
109 | /* To be safer with BIOS, also use erase mark as discriminant */ | 114 | /* To be safer with BIOS, also use erase mark as discriminant */ |
110 | if ((ret = mtd->read_oob(mtd, block * inftl->EraseSize + | 115 | if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize + |
111 | SECTORSIZE + 8, 8, &retlen, | 116 | SECTORSIZE + 8, 8, &retlen, |
112 | (char *)&h1) < 0)) { | 117 | (char *)&h1) < 0)) { |
113 | printk(KERN_WARNING "INFTL: ANAND header found at " | 118 | printk(KERN_WARNING "INFTL: ANAND header found at " |
114 | "0x%x in mtd%d, but OOB data read failed " | 119 | "0x%x in mtd%d, but OOB data read failed " |
115 | "(err %d)\n", block * inftl->EraseSize, | 120 | "(err %d)\n", block * inftl->EraseSize, |
@@ -363,8 +368,8 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, | |||
363 | return -1; | 368 | return -1; |
364 | 369 | ||
365 | if (check_oob) { | 370 | if (check_oob) { |
366 | if(mtd->read_oob(mtd, address, mtd->oobsize, | 371 | if(inftl_read_oob(mtd, address, mtd->oobsize, |
367 | &retlen, &buf[SECTORSIZE]) < 0) | 372 | &retlen, &buf[SECTORSIZE]) < 0) |
368 | return -1; | 373 | return -1; |
369 | if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) | 374 | if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) |
370 | return -1; | 375 | return -1; |
@@ -433,7 +438,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) | |||
433 | uci.Reserved[2] = 0; | 438 | uci.Reserved[2] = 0; |
434 | uci.Reserved[3] = 0; | 439 | uci.Reserved[3] = 0; |
435 | instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; | 440 | instr->addr = block * inftl->EraseSize + SECTORSIZE * 2; |
436 | if (mtd->write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) | 441 | if (inftl_write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0) |
437 | goto fail; | 442 | goto fail; |
438 | return 0; | 443 | return 0; |
439 | fail: | 444 | fail: |
@@ -611,11 +616,11 @@ int INFTL_mount(struct INFTLrecord *s) | |||
611 | break; | 616 | break; |
612 | } | 617 | } |
613 | 618 | ||
614 | if (mtd->read_oob(mtd, block * s->EraseSize + 8, | 619 | if (inftl_read_oob(mtd, block * s->EraseSize + 8, |
615 | 8, &retlen, (char *)&h0) < 0 || | 620 | 8, &retlen, (char *)&h0) < 0 || |
616 | mtd->read_oob(mtd, block * s->EraseSize + | 621 | inftl_read_oob(mtd, block * s->EraseSize + |
617 | 2 * SECTORSIZE + 8, 8, &retlen, | 622 | 2 * SECTORSIZE + 8, 8, &retlen, |
618 | (char *)&h1) < 0) { | 623 | (char *)&h1) < 0) { |
619 | /* Should never happen? */ | 624 | /* Should never happen? */ |
620 | do_format_chain++; | 625 | do_format_chain++; |
621 | break; | 626 | break; |
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index b45e7747daa3..7522fc3a2827 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -408,8 +408,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
408 | case MEMWRITEOOB: | 408 | case MEMWRITEOOB: |
409 | { | 409 | { |
410 | struct mtd_oob_buf buf; | 410 | struct mtd_oob_buf buf; |
411 | void *databuf; | 411 | struct mtd_oob_ops ops; |
412 | ssize_t retlen; | ||
413 | 412 | ||
414 | if(!(file->f_mode & 2)) | 413 | if(!(file->f_mode & 2)) |
415 | return -EPERM; | 414 | return -EPERM; |
@@ -417,7 +416,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
417 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) | 416 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) |
418 | return -EFAULT; | 417 | return -EFAULT; |
419 | 418 | ||
420 | if (buf.length > 0x4096) | 419 | if (buf.length > 4096) |
421 | return -EINVAL; | 420 | return -EINVAL; |
422 | 421 | ||
423 | if (!mtd->write_oob) | 422 | if (!mtd->write_oob) |
@@ -429,21 +428,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
429 | if (ret) | 428 | if (ret) |
430 | return ret; | 429 | return ret; |
431 | 430 | ||
432 | databuf = kmalloc(buf.length, GFP_KERNEL); | 431 | ops.len = buf.length; |
433 | if (!databuf) | 432 | ops.ooblen = mtd->oobsize; |
433 | ops.ooboffs = buf.start & (mtd->oobsize - 1); | ||
434 | ops.datbuf = NULL; | ||
435 | ops.mode = MTD_OOB_PLACE; | ||
436 | |||
437 | if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs)) | ||
438 | return -EINVAL; | ||
439 | |||
440 | ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); | ||
441 | if (!ops.oobbuf) | ||
434 | return -ENOMEM; | 442 | return -ENOMEM; |
435 | 443 | ||
436 | if (copy_from_user(databuf, buf.ptr, buf.length)) { | 444 | if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { |
437 | kfree(databuf); | 445 | kfree(ops.oobbuf); |
438 | return -EFAULT; | 446 | return -EFAULT; |
439 | } | 447 | } |
440 | 448 | ||
441 | ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); | 449 | buf.start &= ~(mtd->oobsize - 1); |
450 | ret = mtd->write_oob(mtd, buf.start, &ops); | ||
442 | 451 | ||
443 | if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t))) | 452 | if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen, |
453 | sizeof(uint32_t))) | ||
444 | ret = -EFAULT; | 454 | ret = -EFAULT; |
445 | 455 | ||
446 | kfree(databuf); | 456 | kfree(ops.oobbuf); |
447 | break; | 457 | break; |
448 | 458 | ||
449 | } | 459 | } |
@@ -451,13 +461,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
451 | case MEMREADOOB: | 461 | case MEMREADOOB: |
452 | { | 462 | { |
453 | struct mtd_oob_buf buf; | 463 | struct mtd_oob_buf buf; |
454 | void *databuf; | 464 | struct mtd_oob_ops ops; |
455 | ssize_t retlen; | ||
456 | 465 | ||
457 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) | 466 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) |
458 | return -EFAULT; | 467 | return -EFAULT; |
459 | 468 | ||
460 | if (buf.length > 0x4096) | 469 | if (buf.length > 4096) |
461 | return -EINVAL; | 470 | return -EINVAL; |
462 | 471 | ||
463 | if (!mtd->read_oob) | 472 | if (!mtd->read_oob) |
@@ -465,22 +474,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
465 | else | 474 | else |
466 | ret = access_ok(VERIFY_WRITE, buf.ptr, | 475 | ret = access_ok(VERIFY_WRITE, buf.ptr, |
467 | buf.length) ? 0 : -EFAULT; | 476 | buf.length) ? 0 : -EFAULT; |
468 | |||
469 | if (ret) | 477 | if (ret) |
470 | return ret; | 478 | return ret; |
471 | 479 | ||
472 | databuf = kmalloc(buf.length, GFP_KERNEL); | 480 | ops.len = buf.length; |
473 | if (!databuf) | 481 | ops.ooblen = mtd->oobsize; |
482 | ops.ooboffs = buf.start & (mtd->oobsize - 1); | ||
483 | ops.datbuf = NULL; | ||
484 | ops.mode = MTD_OOB_PLACE; | ||
485 | |||
486 | if (ops.ooboffs && ops.len > (ops.ooblen - ops.ooboffs)) | ||
487 | return -EINVAL; | ||
488 | |||
489 | ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); | ||
490 | if (!ops.oobbuf) | ||
474 | return -ENOMEM; | 491 | return -ENOMEM; |
475 | 492 | ||
476 | ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); | 493 | buf.start &= ~(mtd->oobsize - 1); |
494 | ret = mtd->read_oob(mtd, buf.start, &ops); | ||
477 | 495 | ||
478 | if (put_user(retlen, (uint32_t __user *)argp)) | 496 | if (put_user(ops.retlen, (uint32_t __user *)argp)) |
479 | ret = -EFAULT; | 497 | ret = -EFAULT; |
480 | else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) | 498 | else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf, |
499 | ops.retlen)) | ||
481 | ret = -EFAULT; | 500 | ret = -EFAULT; |
482 | 501 | ||
483 | kfree(databuf); | 502 | kfree(ops.oobbuf); |
484 | break; | 503 | break; |
485 | } | 504 | } |
486 | 505 | ||
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index ec15abcdbdfa..38151b8e6631 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c | |||
@@ -231,101 +231,85 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, | |||
231 | } | 231 | } |
232 | 232 | ||
233 | static int | 233 | static int |
234 | concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 234 | concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) |
235 | size_t * retlen, u_char * buf) | ||
236 | { | 235 | { |
237 | struct mtd_concat *concat = CONCAT(mtd); | 236 | struct mtd_concat *concat = CONCAT(mtd); |
238 | int err = -EINVAL; | 237 | struct mtd_oob_ops devops = *ops; |
239 | int i; | 238 | int i, err; |
240 | 239 | ||
241 | *retlen = 0; | 240 | ops->retlen = 0; |
242 | 241 | ||
243 | for (i = 0; i < concat->num_subdev; i++) { | 242 | for (i = 0; i < concat->num_subdev; i++) { |
244 | struct mtd_info *subdev = concat->subdev[i]; | 243 | struct mtd_info *subdev = concat->subdev[i]; |
245 | size_t size, retsize; | ||
246 | 244 | ||
247 | if (from >= subdev->size) { | 245 | if (from >= subdev->size) { |
248 | /* Not destined for this subdev */ | ||
249 | size = 0; | ||
250 | from -= subdev->size; | 246 | from -= subdev->size; |
251 | continue; | 247 | continue; |
252 | } | 248 | } |
253 | if (from + len > subdev->size) | ||
254 | /* First part goes into this subdev */ | ||
255 | size = subdev->size - from; | ||
256 | else | ||
257 | /* Entire transaction goes into this subdev */ | ||
258 | size = len; | ||
259 | 249 | ||
260 | if (subdev->read_oob) | 250 | /* partial read ? */ |
261 | err = subdev->read_oob(subdev, from, size, | 251 | if (from + devops.len > subdev->size) |
262 | &retsize, buf); | 252 | devops.len = subdev->size - from; |
263 | else | ||
264 | err = -EINVAL; | ||
265 | 253 | ||
254 | err = subdev->read_oob(subdev, from, &devops); | ||
255 | ops->retlen += devops.retlen; | ||
266 | if (err) | 256 | if (err) |
267 | break; | 257 | return err; |
268 | 258 | ||
269 | *retlen += retsize; | 259 | devops.len = ops->len - ops->retlen; |
270 | len -= size; | 260 | if (!devops.len) |
271 | if (len == 0) | 261 | return 0; |
272 | break; | 262 | |
263 | if (devops.datbuf) | ||
264 | devops.datbuf += devops.retlen; | ||
265 | if (devops.oobbuf) | ||
266 | devops.oobbuf += devops.ooblen; | ||
273 | 267 | ||
274 | err = -EINVAL; | ||
275 | buf += size; | ||
276 | from = 0; | 268 | from = 0; |
277 | } | 269 | } |
278 | return err; | 270 | return -EINVAL; |
279 | } | 271 | } |
280 | 272 | ||
281 | static int | 273 | static int |
282 | concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | 274 | concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) |
283 | size_t * retlen, const u_char * buf) | ||
284 | { | 275 | { |
285 | struct mtd_concat *concat = CONCAT(mtd); | 276 | struct mtd_concat *concat = CONCAT(mtd); |
286 | int err = -EINVAL; | 277 | struct mtd_oob_ops devops = *ops; |
287 | int i; | 278 | int i, err; |
288 | 279 | ||
289 | if (!(mtd->flags & MTD_WRITEABLE)) | 280 | if (!(mtd->flags & MTD_WRITEABLE)) |
290 | return -EROFS; | 281 | return -EROFS; |
291 | 282 | ||
292 | *retlen = 0; | 283 | ops->retlen = 0; |
293 | 284 | ||
294 | for (i = 0; i < concat->num_subdev; i++) { | 285 | for (i = 0; i < concat->num_subdev; i++) { |
295 | struct mtd_info *subdev = concat->subdev[i]; | 286 | struct mtd_info *subdev = concat->subdev[i]; |
296 | size_t size, retsize; | ||
297 | 287 | ||
298 | if (to >= subdev->size) { | 288 | if (to >= subdev->size) { |
299 | size = 0; | ||
300 | to -= subdev->size; | 289 | to -= subdev->size; |
301 | continue; | 290 | continue; |
302 | } | 291 | } |
303 | if (to + len > subdev->size) | ||
304 | size = subdev->size - to; | ||
305 | else | ||
306 | size = len; | ||
307 | 292 | ||
308 | if (!(subdev->flags & MTD_WRITEABLE)) | 293 | /* partial write ? */ |
309 | err = -EROFS; | 294 | if (to + devops.len > subdev->size) |
310 | else if (subdev->write_oob) | 295 | devops.len = subdev->size - to; |
311 | err = subdev->write_oob(subdev, to, size, &retsize, | ||
312 | buf); | ||
313 | else | ||
314 | err = -EINVAL; | ||
315 | 296 | ||
297 | err = subdev->write_oob(subdev, to, &devops); | ||
298 | ops->retlen += devops.retlen; | ||
316 | if (err) | 299 | if (err) |
317 | break; | 300 | return err; |
318 | 301 | ||
319 | *retlen += retsize; | 302 | devops.len = ops->len - ops->retlen; |
320 | len -= size; | 303 | if (!devops.len) |
321 | if (len == 0) | 304 | return 0; |
322 | break; | ||
323 | 305 | ||
324 | err = -EINVAL; | 306 | if (devops.datbuf) |
325 | buf += size; | 307 | devops.datbuf += devops.retlen; |
308 | if (devops.oobbuf) | ||
309 | devops.oobbuf += devops.ooblen; | ||
326 | to = 0; | 310 | to = 0; |
327 | } | 311 | } |
328 | return err; | 312 | return -EINVAL; |
329 | } | 313 | } |
330 | 314 | ||
331 | static void concat_erase_callback(struct erase_info *instr) | 315 | static void concat_erase_callback(struct erase_info *instr) |
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 6d7639b98eab..f22aeccf01e7 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
@@ -78,16 +78,16 @@ static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_ | |||
78 | part->master->unpoint (part->master, addr, from + part->offset, len); | 78 | part->master->unpoint (part->master, addr, from + part->offset, len); |
79 | } | 79 | } |
80 | 80 | ||
81 | static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len, | 81 | static int part_read_oob(struct mtd_info *mtd, loff_t from, |
82 | size_t *retlen, u_char *buf) | 82 | struct mtd_oob_ops *ops) |
83 | { | 83 | { |
84 | struct mtd_part *part = PART(mtd); | 84 | struct mtd_part *part = PART(mtd); |
85 | |||
85 | if (from >= mtd->size) | 86 | if (from >= mtd->size) |
86 | len = 0; | 87 | return -EINVAL; |
87 | else if (from + len > mtd->size) | 88 | if (from + ops->len > mtd->size) |
88 | len = mtd->size - from; | 89 | return -EINVAL; |
89 | return part->master->read_oob (part->master, from + part->offset, | 90 | return part->master->read_oob(part->master, from + part->offset, ops); |
90 | len, retlen, buf); | ||
91 | } | 91 | } |
92 | 92 | ||
93 | static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, | 93 | static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, |
@@ -134,18 +134,19 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, | |||
134 | len, retlen, buf); | 134 | len, retlen, buf); |
135 | } | 135 | } |
136 | 136 | ||
137 | static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len, | 137 | static int part_write_oob(struct mtd_info *mtd, loff_t to, |
138 | size_t *retlen, const u_char *buf) | 138 | struct mtd_oob_ops *ops) |
139 | { | 139 | { |
140 | struct mtd_part *part = PART(mtd); | 140 | struct mtd_part *part = PART(mtd); |
141 | |||
141 | if (!(mtd->flags & MTD_WRITEABLE)) | 142 | if (!(mtd->flags & MTD_WRITEABLE)) |
142 | return -EROFS; | 143 | return -EROFS; |
144 | |||
143 | if (to >= mtd->size) | 145 | if (to >= mtd->size) |
144 | len = 0; | 146 | return -EINVAL; |
145 | else if (to + len > mtd->size) | 147 | if (to + ops->len > mtd->size) |
146 | len = mtd->size - to; | 148 | return -EINVAL; |
147 | return part->master->write_oob (part->master, to + part->offset, | 149 | return part->master->write_oob(part->master, to + part->offset, ops); |
148 | len, retlen, buf); | ||
149 | } | 150 | } |
150 | 151 | ||
151 | static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, | 152 | static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, |
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e922b829c4be..b8e6e1579cf1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -81,23 +81,12 @@ static struct nand_ecclayout nand_oob_64 = { | |||
81 | .length = 38}} | 81 | .length = 38}} |
82 | }; | 82 | }; |
83 | 83 | ||
84 | /* This is used for padding purposes in nand_write_oob */ | ||
85 | static uint8_t ffchars[] = { | ||
86 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
87 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
88 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
89 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
90 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
91 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
92 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
93 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
94 | }; | ||
95 | |||
96 | static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | ||
97 | size_t *retlen, const uint8_t *buf); | ||
98 | static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, | 84 | static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, |
99 | int new_state); | 85 | int new_state); |
100 | 86 | ||
87 | static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | ||
88 | struct mtd_oob_ops *ops); | ||
89 | |||
101 | /* | 90 | /* |
102 | * For devices which display every fart in the system on a seperate LED. Is | 91 | * For devices which display every fart in the system on a seperate LED. Is |
103 | * compiled away when LED support is disabled. | 92 | * compiled away when LED support is disabled. |
@@ -358,7 +347,6 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
358 | { | 347 | { |
359 | struct nand_chip *chip = mtd->priv; | 348 | struct nand_chip *chip = mtd->priv; |
360 | uint8_t buf[2] = { 0, 0 }; | 349 | uint8_t buf[2] = { 0, 0 }; |
361 | size_t retlen; | ||
362 | int block; | 350 | int block; |
363 | 351 | ||
364 | /* Get block number */ | 352 | /* Get block number */ |
@@ -371,8 +359,13 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
371 | return nand_update_bbt(mtd, ofs); | 359 | return nand_update_bbt(mtd, ofs); |
372 | 360 | ||
373 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 361 | /* We write two bytes, so we dont have to mess with 16 bit access */ |
374 | ofs += mtd->oobsize + (chip->badblockpos & ~0x01); | 362 | ofs += mtd->oobsize; |
375 | return nand_write_oob(mtd, ofs, 2, &retlen, buf); | 363 | chip->ops.len = 2; |
364 | chip->ops.datbuf = NULL; | ||
365 | chip->ops.oobbuf = buf; | ||
366 | chip->ops.ooboffs = chip->badblockpos & ~0x01; | ||
367 | |||
368 | return nand_do_write_oob(mtd, ofs, &chip->ops); | ||
376 | } | 369 | } |
377 | 370 | ||
378 | /** | 371 | /** |
@@ -740,6 +733,20 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip, int state) | |||
740 | } | 733 | } |
741 | 734 | ||
742 | /** | 735 | /** |
736 | * nand_read_page_raw - [Intern] read raw page data without ecc | ||
737 | * @mtd: mtd info structure | ||
738 | * @chip: nand chip info structure | ||
739 | * @buf: buffer to store read data | ||
740 | */ | ||
741 | static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | ||
742 | uint8_t *buf) | ||
743 | { | ||
744 | chip->read_buf(mtd, buf, mtd->writesize); | ||
745 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | /** | ||
743 | * nand_read_page_swecc - {REPLACABLE] software ecc based page read function | 750 | * nand_read_page_swecc - {REPLACABLE] software ecc based page read function |
744 | * @mtd: mtd info structure | 751 | * @mtd: mtd info structure |
745 | * @chip: nand chip info structure | 752 | * @chip: nand chip info structure |
@@ -756,11 +763,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
756 | uint8_t *ecc_code = chip->buffers.ecccode; | 763 | uint8_t *ecc_code = chip->buffers.ecccode; |
757 | int *eccpos = chip->ecc.layout->eccpos; | 764 | int *eccpos = chip->ecc.layout->eccpos; |
758 | 765 | ||
759 | chip->read_buf(mtd, buf, mtd->writesize); | 766 | nand_read_page_raw(mtd, chip, buf); |
760 | chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
761 | |||
762 | if (chip->ecc.mode == NAND_ECC_NONE) | ||
763 | return 0; | ||
764 | 767 | ||
765 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) | 768 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) |
766 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); | 769 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); |
@@ -882,18 +885,50 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, | |||
882 | } | 885 | } |
883 | 886 | ||
884 | /** | 887 | /** |
885 | * nand_do_read - [Internal] Read data with ECC | 888 | * nand_transfer_oob - [Internal] Transfer oob to client buffer |
889 | * @chip: nand chip structure | ||
890 | * @ops: oob ops structure | ||
891 | */ | ||
892 | static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, | ||
893 | struct mtd_oob_ops *ops) | ||
894 | { | ||
895 | size_t len = ops->ooblen; | ||
896 | |||
897 | switch(ops->mode) { | ||
898 | |||
899 | case MTD_OOB_PLACE: | ||
900 | case MTD_OOB_RAW: | ||
901 | memcpy(oob, chip->oob_poi + ops->ooboffs, len); | ||
902 | return oob + len; | ||
903 | |||
904 | case MTD_OOB_AUTO: { | ||
905 | struct nand_oobfree *free = chip->ecc.layout->oobfree; | ||
906 | size_t bytes; | ||
907 | |||
908 | for(; free->length && len; free++, len -= bytes) { | ||
909 | bytes = min(len, free->length); | ||
910 | |||
911 | memcpy(oob, chip->oob_poi + free->offset, bytes); | ||
912 | oob += bytes; | ||
913 | } | ||
914 | return oob; | ||
915 | } | ||
916 | default: | ||
917 | BUG(); | ||
918 | } | ||
919 | return NULL; | ||
920 | } | ||
921 | |||
922 | /** | ||
923 | * nand_do_read_ops - [Internal] Read data with ECC | ||
886 | * | 924 | * |
887 | * @mtd: MTD device structure | 925 | * @mtd: MTD device structure |
888 | * @from: offset to read from | 926 | * @from: offset to read from |
889 | * @len: number of bytes to read | ||
890 | * @retlen: pointer to variable to store the number of read bytes | ||
891 | * @buf: the databuffer to put data | ||
892 | * | 927 | * |
893 | * Internal function. Called with chip held. | 928 | * Internal function. Called with chip held. |
894 | */ | 929 | */ |
895 | int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, | 930 | static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, |
896 | size_t *retlen, uint8_t *buf) | 931 | struct mtd_oob_ops *ops) |
897 | { | 932 | { |
898 | int chipnr, page, realpage, col, bytes, aligned; | 933 | int chipnr, page, realpage, col, bytes, aligned; |
899 | struct nand_chip *chip = mtd->priv; | 934 | struct nand_chip *chip = mtd->priv; |
@@ -901,8 +936,8 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
901 | int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; | 936 | int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; |
902 | int sndcmd = 1; | 937 | int sndcmd = 1; |
903 | int ret = 0; | 938 | int ret = 0; |
904 | uint32_t readlen = len; | 939 | uint32_t readlen = ops->len; |
905 | uint8_t *bufpoi; | 940 | uint8_t *bufpoi, *oob, *buf; |
906 | 941 | ||
907 | stats = mtd->ecc_stats; | 942 | stats = mtd->ecc_stats; |
908 | 943 | ||
@@ -915,12 +950,15 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
915 | col = (int)(from & (mtd->writesize - 1)); | 950 | col = (int)(from & (mtd->writesize - 1)); |
916 | chip->oob_poi = chip->buffers.oobrbuf; | 951 | chip->oob_poi = chip->buffers.oobrbuf; |
917 | 952 | ||
953 | buf = ops->datbuf; | ||
954 | oob = ops->oobbuf; | ||
955 | |||
918 | while(1) { | 956 | while(1) { |
919 | bytes = min(mtd->writesize - col, readlen); | 957 | bytes = min(mtd->writesize - col, readlen); |
920 | aligned = (bytes == mtd->writesize); | 958 | aligned = (bytes == mtd->writesize); |
921 | 959 | ||
922 | /* Is the current page in the buffer ? */ | 960 | /* Is the current page in the buffer ? */ |
923 | if (realpage != chip->pagebuf) { | 961 | if (realpage != chip->pagebuf || oob) { |
924 | bufpoi = aligned ? buf : chip->buffers.databuf; | 962 | bufpoi = aligned ? buf : chip->buffers.databuf; |
925 | 963 | ||
926 | if (likely(sndcmd)) { | 964 | if (likely(sndcmd)) { |
@@ -939,6 +977,16 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
939 | memcpy(buf, chip->buffers.databuf + col, bytes); | 977 | memcpy(buf, chip->buffers.databuf + col, bytes); |
940 | } | 978 | } |
941 | 979 | ||
980 | buf += bytes; | ||
981 | |||
982 | if (unlikely(oob)) { | ||
983 | /* Raw mode does data:oob:data:oob */ | ||
984 | if (ops->mode != MTD_OOB_RAW) | ||
985 | oob = nand_transfer_oob(chip, oob, ops); | ||
986 | else | ||
987 | buf = nand_transfer_oob(chip, buf, ops); | ||
988 | } | ||
989 | |||
942 | if (!(chip->options & NAND_NO_READRDY)) { | 990 | if (!(chip->options & NAND_NO_READRDY)) { |
943 | /* | 991 | /* |
944 | * Apply delay or wait for ready/busy pin. Do | 992 | * Apply delay or wait for ready/busy pin. Do |
@@ -952,10 +1000,11 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
952 | else | 1000 | else |
953 | nand_wait_ready(mtd); | 1001 | nand_wait_ready(mtd); |
954 | } | 1002 | } |
955 | } else | 1003 | } else { |
956 | memcpy(buf, chip->buffers.databuf + col, bytes); | 1004 | memcpy(buf, chip->buffers.databuf + col, bytes); |
1005 | buf += bytes; | ||
1006 | } | ||
957 | 1007 | ||
958 | buf += bytes; | ||
959 | readlen -= bytes; | 1008 | readlen -= bytes; |
960 | 1009 | ||
961 | if (!readlen) | 1010 | if (!readlen) |
@@ -981,7 +1030,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
981 | sndcmd = 1; | 1030 | sndcmd = 1; |
982 | } | 1031 | } |
983 | 1032 | ||
984 | *retlen = len - (size_t) readlen; | 1033 | ops->retlen = ops->len - (size_t) readlen; |
985 | 1034 | ||
986 | if (ret) | 1035 | if (ret) |
987 | return ret; | 1036 | return ret; |
@@ -1002,57 +1051,49 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
1002 | static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, | 1051 | static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, |
1003 | size_t *retlen, uint8_t *buf) | 1052 | size_t *retlen, uint8_t *buf) |
1004 | { | 1053 | { |
1054 | struct nand_chip *chip = mtd->priv; | ||
1005 | int ret; | 1055 | int ret; |
1006 | 1056 | ||
1007 | *retlen = 0; | ||
1008 | /* Do not allow reads past end of device */ | 1057 | /* Do not allow reads past end of device */ |
1009 | if ((from + len) > mtd->size) | 1058 | if ((from + len) > mtd->size) |
1010 | return -EINVAL; | 1059 | return -EINVAL; |
1011 | if (!len) | 1060 | if (!len) |
1012 | return 0; | 1061 | return 0; |
1013 | 1062 | ||
1014 | nand_get_device(mtd->priv, mtd, FL_READING); | 1063 | nand_get_device(chip, mtd, FL_READING); |
1015 | 1064 | ||
1016 | ret = nand_do_read(mtd, from, len, retlen, buf); | 1065 | chip->ops.len = len; |
1066 | chip->ops.datbuf = buf; | ||
1067 | chip->ops.oobbuf = NULL; | ||
1068 | |||
1069 | ret = nand_do_read_ops(mtd, from, &chip->ops); | ||
1017 | 1070 | ||
1018 | nand_release_device(mtd); | 1071 | nand_release_device(mtd); |
1019 | 1072 | ||
1073 | *retlen = chip->ops.retlen; | ||
1020 | return ret; | 1074 | return ret; |
1021 | } | 1075 | } |
1022 | 1076 | ||
1023 | /** | 1077 | /** |
1024 | * nand_read_oob - [MTD Interface] NAND read out-of-band | 1078 | * nand_do_read_oob - [Intern] NAND read out-of-band |
1025 | * @mtd: MTD device structure | 1079 | * @mtd: MTD device structure |
1026 | * @from: offset to read from | 1080 | * @from: offset to read from |
1027 | * @len: number of bytes to read | 1081 | * @ops: oob operations description structure |
1028 | * @retlen: pointer to variable to store the number of read bytes | ||
1029 | * @buf: the databuffer to put data | ||
1030 | * | 1082 | * |
1031 | * NAND read out-of-band data from the spare area | 1083 | * NAND read out-of-band data from the spare area |
1032 | */ | 1084 | */ |
1033 | static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 1085 | static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, |
1034 | size_t *retlen, uint8_t *buf) | 1086 | struct mtd_oob_ops *ops) |
1035 | { | 1087 | { |
1036 | int col, page, realpage, chipnr, sndcmd = 1; | 1088 | int col, page, realpage, chipnr, sndcmd = 1; |
1037 | struct nand_chip *chip = mtd->priv; | 1089 | struct nand_chip *chip = mtd->priv; |
1038 | int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; | 1090 | int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; |
1039 | int readlen = len; | 1091 | int direct, bytes, readlen = ops->len; |
1092 | uint8_t *bufpoi, *buf = ops->oobbuf; | ||
1040 | 1093 | ||
1041 | DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", | 1094 | DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", |
1042 | (unsigned int)from, (int)len); | 1095 | (unsigned int)from, (int)len); |
1043 | 1096 | ||
1044 | /* Initialize return length value */ | ||
1045 | *retlen = 0; | ||
1046 | |||
1047 | /* Do not allow reads past end of device */ | ||
1048 | if ((from + len) > mtd->size) { | ||
1049 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
1050 | "Attempt read beyond end of device\n"); | ||
1051 | return -EINVAL; | ||
1052 | } | ||
1053 | |||
1054 | nand_get_device(chip, mtd, FL_READING); | ||
1055 | |||
1056 | chipnr = (int)(from >> chip->chip_shift); | 1097 | chipnr = (int)(from >> chip->chip_shift); |
1057 | chip->select_chip(mtd, chipnr); | 1098 | chip->select_chip(mtd, chipnr); |
1058 | 1099 | ||
@@ -1060,20 +1101,31 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
1060 | realpage = (int)(from >> chip->page_shift); | 1101 | realpage = (int)(from >> chip->page_shift); |
1061 | page = realpage & chip->pagemask; | 1102 | page = realpage & chip->pagemask; |
1062 | 1103 | ||
1063 | /* Mask to get column */ | 1104 | if (ops->mode != MTD_OOB_AUTO) { |
1064 | col = from & (mtd->oobsize - 1); | 1105 | col = ops->ooboffs; |
1106 | direct = 1; | ||
1107 | } else { | ||
1108 | col = 0; | ||
1109 | direct = 0; | ||
1110 | } | ||
1065 | 1111 | ||
1066 | while(1) { | 1112 | while(1) { |
1067 | int bytes = min((int)(mtd->oobsize - col), readlen); | 1113 | bytes = direct ? ops->ooblen : mtd->oobsize; |
1114 | bufpoi = direct ? buf : chip->buffers.oobrbuf; | ||
1068 | 1115 | ||
1069 | if (likely(sndcmd)) { | 1116 | if (likely(sndcmd)) { |
1070 | chip->cmdfunc(mtd, NAND_CMD_READOOB, col, page); | 1117 | chip->cmdfunc(mtd, NAND_CMD_READOOB, col, page); |
1071 | sndcmd = 0; | 1118 | sndcmd = 0; |
1072 | } | 1119 | } |
1073 | 1120 | ||
1074 | chip->read_buf(mtd, buf, bytes); | 1121 | chip->read_buf(mtd, bufpoi, bytes); |
1075 | 1122 | ||
1076 | readlen -= bytes; | 1123 | if (unlikely(!direct)) |
1124 | buf = nand_transfer_oob(chip, buf, ops); | ||
1125 | else | ||
1126 | buf += ops->ooblen; | ||
1127 | |||
1128 | readlen -= ops->ooblen; | ||
1077 | if (!readlen) | 1129 | if (!readlen) |
1078 | break; | 1130 | break; |
1079 | 1131 | ||
@@ -1090,10 +1142,6 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
1090 | nand_wait_ready(mtd); | 1142 | nand_wait_ready(mtd); |
1091 | } | 1143 | } |
1092 | 1144 | ||
1093 | buf += bytes; | ||
1094 | bytes = mtd->oobsize; | ||
1095 | col = 0; | ||
1096 | |||
1097 | /* Increment page address */ | 1145 | /* Increment page address */ |
1098 | realpage++; | 1146 | realpage++; |
1099 | 1147 | ||
@@ -1112,81 +1160,76 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
1112 | sndcmd = 1; | 1160 | sndcmd = 1; |
1113 | } | 1161 | } |
1114 | 1162 | ||
1115 | /* Deselect and wake up anyone waiting on the device */ | 1163 | ops->retlen = ops->len; |
1116 | nand_release_device(mtd); | ||
1117 | |||
1118 | *retlen = len; | ||
1119 | return 0; | 1164 | return 0; |
1120 | } | 1165 | } |
1121 | 1166 | ||
1122 | /** | 1167 | /** |
1123 | * nand_read_raw - [GENERIC] Read raw data including oob into buffer | 1168 | * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band |
1124 | * @mtd: MTD device structure | 1169 | * @mtd: MTD device structure |
1125 | * @buf: temporary buffer | ||
1126 | * @from: offset to read from | 1170 | * @from: offset to read from |
1127 | * @len: number of bytes to read | 1171 | * @ops: oob operation description structure |
1128 | * @ooblen: number of oob data bytes to read | ||
1129 | * | 1172 | * |
1130 | * Read raw data including oob into buffer | 1173 | * NAND read data and/or out-of-band data |
1131 | */ | 1174 | */ |
1132 | int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, | 1175 | static int nand_read_oob(struct mtd_info *mtd, loff_t from, |
1133 | size_t ooblen) | 1176 | struct mtd_oob_ops *ops) |
1134 | { | 1177 | { |
1178 | int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, | ||
1179 | uint8_t *buf) = NULL; | ||
1135 | struct nand_chip *chip = mtd->priv; | 1180 | struct nand_chip *chip = mtd->priv; |
1136 | int page = (int)(from >> chip->page_shift); | 1181 | int ret = -ENOTSUPP; |
1137 | int chipnr = (int)(from >> chip->chip_shift); | 1182 | |
1138 | int sndcmd = 1; | 1183 | ops->retlen = 0; |
1139 | int cnt = 0; | ||
1140 | int pagesize = mtd->writesize + mtd->oobsize; | ||
1141 | int blockcheck; | ||
1142 | 1184 | ||
1143 | /* Do not allow reads past end of device */ | 1185 | /* Do not allow reads past end of device */ |
1144 | if ((from + len) > mtd->size) { | 1186 | if ((from + ops->len) > mtd->size) { |
1145 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: " | 1187 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " |
1146 | "Attempt read beyond end of device\n"); | 1188 | "Attempt read beyond end of device\n"); |
1147 | return -EINVAL; | 1189 | return -EINVAL; |
1148 | } | 1190 | } |
1149 | 1191 | ||
1150 | /* Grab the lock and see if the device is available */ | ||
1151 | nand_get_device(chip, mtd, FL_READING); | 1192 | nand_get_device(chip, mtd, FL_READING); |
1152 | 1193 | ||
1153 | chip->select_chip(mtd, chipnr); | 1194 | switch(ops->mode) { |
1154 | 1195 | case MTD_OOB_PLACE: | |
1155 | /* Add requested oob length */ | 1196 | case MTD_OOB_AUTO: |
1156 | len += ooblen; | 1197 | break; |
1157 | blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; | ||
1158 | 1198 | ||
1159 | while (len) { | 1199 | case MTD_OOB_RAW: |
1160 | if (likely(sndcmd)) { | 1200 | /* Replace the read_page algorithm temporary */ |
1161 | chip->cmdfunc(mtd, NAND_CMD_READ0, 0, | 1201 | read_page = chip->ecc.read_page; |
1162 | page & chip->pagemask); | 1202 | chip->ecc.read_page = nand_read_page_raw; |
1163 | sndcmd = 0; | 1203 | break; |
1164 | } | ||
1165 | 1204 | ||
1166 | chip->read_buf(mtd, &buf[cnt], pagesize); | 1205 | default: |
1206 | goto out; | ||
1207 | } | ||
1167 | 1208 | ||
1168 | len -= pagesize; | 1209 | if (!ops->datbuf) |
1169 | cnt += pagesize; | 1210 | ret = nand_do_read_oob(mtd, from, ops); |
1170 | page++; | 1211 | else |
1212 | ret = nand_do_read_ops(mtd, from, ops); | ||
1171 | 1213 | ||
1172 | if (!(chip->options & NAND_NO_READRDY)) { | 1214 | if (unlikely(ops->mode == MTD_OOB_RAW)) |
1173 | if (!chip->dev_ready) | 1215 | chip->ecc.read_page = read_page; |
1174 | udelay(chip->chip_delay); | 1216 | out: |
1175 | else | 1217 | nand_release_device(mtd); |
1176 | nand_wait_ready(mtd); | 1218 | return ret; |
1177 | } | 1219 | } |
1178 | 1220 | ||
1179 | /* | ||
1180 | * Check, if the chip supports auto page increment or if we | ||
1181 | * cross a block boundary. | ||
1182 | */ | ||
1183 | if (!NAND_CANAUTOINCR(chip) || !(page & blockcheck)) | ||
1184 | sndcmd = 1; | ||
1185 | } | ||
1186 | 1221 | ||
1187 | /* Deselect and wake up anyone waiting on the device */ | 1222 | /** |
1188 | nand_release_device(mtd); | 1223 | * nand_write_page_raw - [Intern] raw page write function |
1189 | return 0; | 1224 | * @mtd: mtd info structure |
1225 | * @chip: nand chip info structure | ||
1226 | * @buf: data buffer | ||
1227 | */ | ||
1228 | static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | ||
1229 | const uint8_t *buf) | ||
1230 | { | ||
1231 | chip->write_buf(mtd, buf, mtd->writesize); | ||
1232 | chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
1190 | } | 1233 | } |
1191 | 1234 | ||
1192 | /** | 1235 | /** |
@@ -1205,17 +1248,14 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
1205 | const uint8_t *p = buf; | 1248 | const uint8_t *p = buf; |
1206 | int *eccpos = chip->ecc.layout->eccpos; | 1249 | int *eccpos = chip->ecc.layout->eccpos; |
1207 | 1250 | ||
1208 | if (chip->ecc.mode != NAND_ECC_NONE) { | 1251 | /* Software ecc calculation */ |
1209 | /* Software ecc calculation */ | 1252 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) |
1210 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) | 1253 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); |
1211 | chip->ecc.calculate(mtd, p, &ecc_calc[i]); | ||
1212 | 1254 | ||
1213 | for (i = 0; i < chip->ecc.total; i++) | 1255 | for (i = 0; i < chip->ecc.total; i++) |
1214 | chip->oob_poi[eccpos[i]] = ecc_calc[i]; | 1256 | chip->oob_poi[eccpos[i]] = ecc_calc[i]; |
1215 | } | ||
1216 | 1257 | ||
1217 | chip->write_buf(mtd, buf, mtd->writesize); | 1258 | nand_write_page_raw(mtd, chip, buf); |
1218 | chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); | ||
1219 | } | 1259 | } |
1220 | 1260 | ||
1221 | /** | 1261 | /** |
@@ -1342,51 +1382,77 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, | |||
1342 | return 0; | 1382 | return 0; |
1343 | } | 1383 | } |
1344 | 1384 | ||
1385 | /** | ||
1386 | * nand_fill_oob - [Internal] Transfer client buffer to oob | ||
1387 | * @chip: nand chip structure | ||
1388 | * @oob: oob data buffer | ||
1389 | * @ops: oob ops structure | ||
1390 | */ | ||
1391 | static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, | ||
1392 | struct mtd_oob_ops *ops) | ||
1393 | { | ||
1394 | size_t len = ops->ooblen; | ||
1395 | |||
1396 | switch(ops->mode) { | ||
1397 | |||
1398 | case MTD_OOB_PLACE: | ||
1399 | case MTD_OOB_RAW: | ||
1400 | memcpy(chip->oob_poi + ops->ooboffs, oob, len); | ||
1401 | return oob + len; | ||
1402 | |||
1403 | case MTD_OOB_AUTO: { | ||
1404 | struct nand_oobfree *free = chip->ecc.layout->oobfree; | ||
1405 | size_t bytes; | ||
1406 | |||
1407 | for(; free->length && len; free++, len -= bytes) { | ||
1408 | bytes = min(len, free->length); | ||
1409 | memcpy(chip->oob_poi + free->offset, oob, bytes); | ||
1410 | oob += bytes; | ||
1411 | } | ||
1412 | return oob; | ||
1413 | } | ||
1414 | default: | ||
1415 | BUG(); | ||
1416 | } | ||
1417 | return NULL; | ||
1418 | } | ||
1419 | |||
1345 | #define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0 | 1420 | #define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0 |
1346 | 1421 | ||
1347 | /** | 1422 | /** |
1348 | * nand_write - [MTD Interface] NAND write with ECC | 1423 | * nand_do_write_ops - [Internal] NAND write with ECC |
1349 | * @mtd: MTD device structure | 1424 | * @mtd: MTD device structure |
1350 | * @to: offset to write to | 1425 | * @to: offset to write to |
1351 | * @len: number of bytes to write | 1426 | * @ops: oob operations description structure |
1352 | * @retlen: pointer to variable to store the number of written bytes | ||
1353 | * @buf: the data to write | ||
1354 | * | 1427 | * |
1355 | * NAND write with ECC | 1428 | * NAND write with ECC |
1356 | */ | 1429 | */ |
1357 | static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, | 1430 | static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, |
1358 | size_t *retlen, const uint8_t *buf) | 1431 | struct mtd_oob_ops *ops) |
1359 | { | 1432 | { |
1360 | int chipnr, realpage, page, blockmask; | 1433 | int chipnr, realpage, page, blockmask; |
1361 | struct nand_chip *chip = mtd->priv; | 1434 | struct nand_chip *chip = mtd->priv; |
1362 | uint32_t writelen = len; | 1435 | uint32_t writelen = ops->len; |
1436 | uint8_t *oob = ops->oobbuf; | ||
1437 | uint8_t *buf = ops->datbuf; | ||
1363 | int bytes = mtd->writesize; | 1438 | int bytes = mtd->writesize; |
1364 | int ret = -EIO; | 1439 | int ret; |
1365 | 1440 | ||
1366 | *retlen = 0; | 1441 | ops->retlen = 0; |
1367 | |||
1368 | /* Do not allow write past end of device */ | ||
1369 | if ((to + len) > mtd->size) { | ||
1370 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write: " | ||
1371 | "Attempt to write past end of page\n"); | ||
1372 | return -EINVAL; | ||
1373 | } | ||
1374 | 1442 | ||
1375 | /* reject writes, which are not page aligned */ | 1443 | /* reject writes, which are not page aligned */ |
1376 | if (NOTALIGNED(to) || NOTALIGNED(len)) { | 1444 | if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { |
1377 | printk(KERN_NOTICE "nand_write: " | 1445 | printk(KERN_NOTICE "nand_write: " |
1378 | "Attempt to write not page aligned data\n"); | 1446 | "Attempt to write not page aligned data\n"); |
1379 | return -EINVAL; | 1447 | return -EINVAL; |
1380 | } | 1448 | } |
1381 | 1449 | ||
1382 | if (!len) | 1450 | if (!writelen) |
1383 | return 0; | 1451 | return 0; |
1384 | 1452 | ||
1385 | nand_get_device(chip, mtd, FL_WRITING); | ||
1386 | |||
1387 | /* Check, if it is write protected */ | 1453 | /* Check, if it is write protected */ |
1388 | if (nand_check_wp(mtd)) | 1454 | if (nand_check_wp(mtd)) |
1389 | goto out; | 1455 | return -EIO; |
1390 | 1456 | ||
1391 | chipnr = (int)(to >> chip->chip_shift); | 1457 | chipnr = (int)(to >> chip->chip_shift); |
1392 | chip->select_chip(mtd, chipnr); | 1458 | chip->select_chip(mtd, chipnr); |
@@ -1397,7 +1463,7 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1397 | 1463 | ||
1398 | /* Invalidate the page cache, when we write to the cached page */ | 1464 | /* Invalidate the page cache, when we write to the cached page */ |
1399 | if (to <= (chip->pagebuf << chip->page_shift) && | 1465 | if (to <= (chip->pagebuf << chip->page_shift) && |
1400 | (chip->pagebuf << chip->page_shift) < (to + len)) | 1466 | (chip->pagebuf << chip->page_shift) < (to + ops->len)) |
1401 | chip->pagebuf = -1; | 1467 | chip->pagebuf = -1; |
1402 | 1468 | ||
1403 | chip->oob_poi = chip->buffers.oobwbuf; | 1469 | chip->oob_poi = chip->buffers.oobwbuf; |
@@ -1405,6 +1471,9 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1405 | while(1) { | 1471 | while(1) { |
1406 | int cached = writelen > bytes && page != blockmask; | 1472 | int cached = writelen > bytes && page != blockmask; |
1407 | 1473 | ||
1474 | if (unlikely(oob)) | ||
1475 | oob = nand_fill_oob(chip, oob, ops); | ||
1476 | |||
1408 | ret = nand_write_page(mtd, chip, buf, page, cached); | 1477 | ret = nand_write_page(mtd, chip, buf, page, cached); |
1409 | if (ret) | 1478 | if (ret) |
1410 | break; | 1479 | break; |
@@ -1424,94 +1493,74 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1424 | chip->select_chip(mtd, chipnr); | 1493 | chip->select_chip(mtd, chipnr); |
1425 | } | 1494 | } |
1426 | } | 1495 | } |
1427 | out: | 1496 | |
1428 | *retlen = len - writelen; | 1497 | if (unlikely(oob)) |
1429 | nand_release_device(mtd); | 1498 | memset(chip->oob_poi, 0xff, mtd->oobsize); |
1499 | |||
1500 | ops->retlen = ops->len - writelen; | ||
1430 | return ret; | 1501 | return ret; |
1431 | } | 1502 | } |
1432 | 1503 | ||
1433 | /** | 1504 | /** |
1434 | * nand_write_raw - [GENERIC] Write raw data including oob | 1505 | * nand_write - [MTD Interface] NAND write with ECC |
1435 | * @mtd: MTD device structure | 1506 | * @mtd: MTD device structure |
1436 | * @buf: source buffer | ||
1437 | * @to: offset to write to | 1507 | * @to: offset to write to |
1438 | * @len: number of bytes to write | 1508 | * @len: number of bytes to write |
1439 | * @buf: source buffer | 1509 | * @retlen: pointer to variable to store the number of written bytes |
1440 | * @oob: oob buffer | 1510 | * @buf: the data to write |
1441 | * | 1511 | * |
1442 | * Write raw data including oob | 1512 | * NAND write with ECC |
1443 | */ | 1513 | */ |
1444 | int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, | 1514 | static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, |
1445 | const uint8_t *buf, uint8_t *oob) | 1515 | size_t *retlen, const uint8_t *buf) |
1446 | { | 1516 | { |
1447 | struct nand_chip *chip = mtd->priv; | 1517 | struct nand_chip *chip = mtd->priv; |
1448 | int page = (int)(to >> chip->page_shift); | ||
1449 | int chipnr = (int)(to >> chip->chip_shift); | ||
1450 | int ret; | 1518 | int ret; |
1451 | 1519 | ||
1452 | *retlen = 0; | 1520 | /* Do not allow reads past end of device */ |
1453 | 1521 | if ((to + len) > mtd->size) | |
1454 | /* Do not allow writes past end of device */ | ||
1455 | if ((to + len) > mtd->size) { | ||
1456 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt write " | ||
1457 | "beyond end of device\n"); | ||
1458 | return -EINVAL; | 1522 | return -EINVAL; |
1459 | } | 1523 | if (!len) |
1524 | return 0; | ||
1460 | 1525 | ||
1461 | /* Grab the lock and see if the device is available */ | 1526 | nand_get_device(chip, mtd, FL_READING); |
1462 | nand_get_device(chip, mtd, FL_WRITING); | ||
1463 | 1527 | ||
1464 | chip->select_chip(mtd, chipnr); | 1528 | chip->ops.len = len; |
1465 | chip->oob_poi = oob; | 1529 | chip->ops.datbuf = (uint8_t *)buf; |
1530 | chip->ops.oobbuf = NULL; | ||
1466 | 1531 | ||
1467 | while (len != *retlen) { | 1532 | ret = nand_do_write_ops(mtd, to, &chip->ops); |
1468 | ret = nand_write_page(mtd, chip, buf, page, 0); | ||
1469 | if (ret) | ||
1470 | return ret; | ||
1471 | page++; | ||
1472 | *retlen += mtd->writesize; | ||
1473 | buf += mtd->writesize; | ||
1474 | chip->oob_poi += mtd->oobsize; | ||
1475 | } | ||
1476 | 1533 | ||
1477 | /* Deselect and wake up anyone waiting on the device */ | ||
1478 | nand_release_device(mtd); | 1534 | nand_release_device(mtd); |
1479 | return 0; | 1535 | |
1536 | *retlen = chip->ops.retlen; | ||
1537 | return ret; | ||
1480 | } | 1538 | } |
1481 | EXPORT_SYMBOL_GPL(nand_write_raw); | ||
1482 | 1539 | ||
1483 | /** | 1540 | /** |
1484 | * nand_write_oob - [MTD Interface] NAND write out-of-band | 1541 | * nand_do_write_oob - [MTD Interface] NAND write out-of-band |
1485 | * @mtd: MTD device structure | 1542 | * @mtd: MTD device structure |
1486 | * @to: offset to write to | 1543 | * @to: offset to write to |
1487 | * @len: number of bytes to write | 1544 | * @ops: oob operation description structure |
1488 | * @retlen: pointer to variable to store the number of written bytes | ||
1489 | * @buf: the data to write | ||
1490 | * | 1545 | * |
1491 | * NAND write out-of-band | 1546 | * NAND write out-of-band |
1492 | */ | 1547 | */ |
1493 | static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | 1548 | static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, |
1494 | size_t *retlen, const uint8_t *buf) | 1549 | struct mtd_oob_ops *ops) |
1495 | { | 1550 | { |
1496 | int column, page, status, ret = -EIO, chipnr; | 1551 | int chipnr, page, status; |
1497 | struct nand_chip *chip = mtd->priv; | 1552 | struct nand_chip *chip = mtd->priv; |
1498 | 1553 | ||
1499 | DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", | 1554 | DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", |
1500 | (unsigned int)to, (int)len); | 1555 | (unsigned int)to, (int)ops->len); |
1501 | |||
1502 | /* Initialize return length value */ | ||
1503 | *retlen = 0; | ||
1504 | 1556 | ||
1505 | /* Do not allow write past end of page */ | 1557 | /* Do not allow write past end of page */ |
1506 | column = to & (mtd->oobsize - 1); | 1558 | if ((ops->ooboffs + ops->len) > mtd->oobsize) { |
1507 | if ((column + len) > mtd->oobsize) { | ||
1508 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " | 1559 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " |
1509 | "Attempt to write past end of page\n"); | 1560 | "Attempt to write past end of page\n"); |
1510 | return -EINVAL; | 1561 | return -EINVAL; |
1511 | } | 1562 | } |
1512 | 1563 | ||
1513 | nand_get_device(chip, mtd, FL_WRITING); | ||
1514 | |||
1515 | chipnr = (int)(to >> chip->chip_shift); | 1564 | chipnr = (int)(to >> chip->chip_shift); |
1516 | chip->select_chip(mtd, chipnr); | 1565 | chip->select_chip(mtd, chipnr); |
1517 | 1566 | ||
@@ -1528,26 +1577,27 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1528 | 1577 | ||
1529 | /* Check, if it is write protected */ | 1578 | /* Check, if it is write protected */ |
1530 | if (nand_check_wp(mtd)) | 1579 | if (nand_check_wp(mtd)) |
1531 | goto out; | 1580 | return -EROFS; |
1532 | 1581 | ||
1533 | /* Invalidate the page cache, if we write to the cached page */ | 1582 | /* Invalidate the page cache, if we write to the cached page */ |
1534 | if (page == chip->pagebuf) | 1583 | if (page == chip->pagebuf) |
1535 | chip->pagebuf = -1; | 1584 | chip->pagebuf = -1; |
1536 | 1585 | ||
1537 | if (NAND_MUST_PAD(chip)) { | 1586 | if (ops->mode == MTD_OOB_AUTO || NAND_MUST_PAD(chip)) { |
1587 | chip->oob_poi = chip->buffers.oobwbuf; | ||
1588 | memset(chip->oob_poi, 0xff, mtd->oobsize); | ||
1589 | nand_fill_oob(chip, ops->oobbuf, ops); | ||
1538 | chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, | 1590 | chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, |
1539 | page & chip->pagemask); | 1591 | page & chip->pagemask); |
1540 | /* prepad 0xff for partial programming */ | 1592 | chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); |
1541 | chip->write_buf(mtd, ffchars, column); | 1593 | memset(chip->oob_poi, 0xff, mtd->oobsize); |
1542 | /* write data */ | ||
1543 | chip->write_buf(mtd, buf, len); | ||
1544 | /* postpad 0xff for partial programming */ | ||
1545 | chip->write_buf(mtd, ffchars, mtd->oobsize - (len + column)); | ||
1546 | } else { | 1594 | } else { |
1547 | chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + column, | 1595 | chip->cmdfunc(mtd, NAND_CMD_SEQIN, |
1596 | mtd->writesize + ops->ooboffs, | ||
1548 | page & chip->pagemask); | 1597 | page & chip->pagemask); |
1549 | chip->write_buf(mtd, buf, len); | 1598 | chip->write_buf(mtd, ops->oobbuf, ops->len); |
1550 | } | 1599 | } |
1600 | |||
1551 | /* Send command to program the OOB data */ | 1601 | /* Send command to program the OOB data */ |
1552 | chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); | 1602 | chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); |
1553 | 1603 | ||
@@ -1557,27 +1607,75 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1557 | if (status & NAND_STATUS_FAIL) { | 1607 | if (status & NAND_STATUS_FAIL) { |
1558 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " | 1608 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " |
1559 | "Failed write, page 0x%08x\n", page); | 1609 | "Failed write, page 0x%08x\n", page); |
1560 | ret = -EIO; | 1610 | return -EIO; |
1561 | goto out; | ||
1562 | } | 1611 | } |
1563 | *retlen = len; | 1612 | ops->retlen = ops->len; |
1564 | 1613 | ||
1565 | #ifdef CONFIG_MTD_NAND_VERIFY_WRITE | 1614 | #ifdef CONFIG_MTD_NAND_VERIFY_WRITE |
1566 | /* Send command to read back the data */ | 1615 | if (ops->mode != MTD_OOB_AUTO) { |
1567 | chip->cmdfunc(mtd, NAND_CMD_READOOB, column, page & chip->pagemask); | 1616 | /* Send command to read back the data */ |
1617 | chip->cmdfunc(mtd, NAND_CMD_READOOB, ops->ooboffs, | ||
1618 | page & chip->pagemask); | ||
1568 | 1619 | ||
1569 | if (chip->verify_buf(mtd, buf, len)) { | 1620 | if (chip->verify_buf(mtd, ops->oobbuf, ops->len)) { |
1570 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " | 1621 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " |
1571 | "Failed write verify, page 0x%08x\n", page); | 1622 | "Failed write verify, page 0x%08x\n", page); |
1572 | ret = -EIO; | 1623 | return -EIO; |
1573 | goto out; | 1624 | } |
1574 | } | 1625 | } |
1575 | #endif | 1626 | #endif |
1576 | ret = 0; | 1627 | return 0; |
1628 | } | ||
1629 | |||
1630 | /** | ||
1631 | * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band | ||
1632 | * @mtd: MTD device structure | ||
1633 | * @from: offset to read from | ||
1634 | * @ops: oob operation description structure | ||
1635 | */ | ||
1636 | static int nand_write_oob(struct mtd_info *mtd, loff_t to, | ||
1637 | struct mtd_oob_ops *ops) | ||
1638 | { | ||
1639 | void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, | ||
1640 | const uint8_t *buf) = NULL; | ||
1641 | struct nand_chip *chip = mtd->priv; | ||
1642 | int ret = -ENOTSUPP; | ||
1643 | |||
1644 | ops->retlen = 0; | ||
1645 | |||
1646 | /* Do not allow writes past end of device */ | ||
1647 | if ((to + ops->len) > mtd->size) { | ||
1648 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | ||
1649 | "Attempt read beyond end of device\n"); | ||
1650 | return -EINVAL; | ||
1651 | } | ||
1652 | |||
1653 | nand_get_device(chip, mtd, FL_READING); | ||
1654 | |||
1655 | switch(ops->mode) { | ||
1656 | case MTD_OOB_PLACE: | ||
1657 | case MTD_OOB_AUTO: | ||
1658 | break; | ||
1659 | |||
1660 | case MTD_OOB_RAW: | ||
1661 | /* Replace the write_page algorithm temporary */ | ||
1662 | write_page = chip->ecc.write_page; | ||
1663 | chip->ecc.write_page = nand_write_page_raw; | ||
1664 | break; | ||
1665 | |||
1666 | default: | ||
1667 | goto out; | ||
1668 | } | ||
1669 | |||
1670 | if (!ops->datbuf) | ||
1671 | ret = nand_do_write_oob(mtd, to, ops); | ||
1672 | else | ||
1673 | ret = nand_do_write_ops(mtd, to, ops); | ||
1674 | |||
1675 | if (unlikely(ops->mode == MTD_OOB_RAW)) | ||
1676 | chip->ecc.write_page = write_page; | ||
1577 | out: | 1677 | out: |
1578 | /* Deselect and wake up anyone waiting on the device */ | ||
1579 | nand_release_device(mtd); | 1678 | nand_release_device(mtd); |
1580 | |||
1581 | return ret; | 1679 | return ret; |
1582 | } | 1680 | } |
1583 | 1681 | ||
@@ -2191,8 +2289,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips) | |||
2191 | case NAND_ECC_NONE: | 2289 | case NAND_ECC_NONE: |
2192 | printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " | 2290 | printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " |
2193 | "This is not recommended !!\n"); | 2291 | "This is not recommended !!\n"); |
2194 | chip->ecc.read_page = nand_read_page_swecc; | 2292 | chip->ecc.read_page = nand_read_page_raw; |
2195 | chip->ecc.write_page = nand_write_page_swecc; | 2293 | chip->ecc.write_page = nand_write_page_raw; |
2196 | chip->ecc.size = mtd->writesize; | 2294 | chip->ecc.size = mtd->writesize; |
2197 | chip->ecc.bytes = 0; | 2295 | chip->ecc.bytes = 0; |
2198 | break; | 2296 | break; |
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 40f99304df76..480c3cbf9bf9 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c | |||
@@ -230,6 +230,42 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc | |||
230 | return 0; | 230 | return 0; |
231 | } | 231 | } |
232 | 232 | ||
233 | /* | ||
234 | * Scan read raw data from flash | ||
235 | */ | ||
236 | static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, | ||
237 | size_t len) | ||
238 | { | ||
239 | struct mtd_oob_ops ops; | ||
240 | |||
241 | ops.mode = MTD_OOB_RAW; | ||
242 | ops.ooboffs = 0; | ||
243 | ops.ooblen = mtd->oobsize; | ||
244 | ops.oobbuf = buf; | ||
245 | ops.datbuf = buf; | ||
246 | ops.len = len; | ||
247 | |||
248 | return mtd->read_oob(mtd, offs, &ops); | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * Scan write data with oob to flash | ||
253 | */ | ||
254 | static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, | ||
255 | uint8_t *buf, uint8_t *oob) | ||
256 | { | ||
257 | struct mtd_oob_ops ops; | ||
258 | |||
259 | ops.mode = MTD_OOB_PLACE; | ||
260 | ops.ooboffs = 0; | ||
261 | ops.ooblen = mtd->oobsize; | ||
262 | ops.datbuf = buf; | ||
263 | ops.oobbuf = oob; | ||
264 | ops.len = len; | ||
265 | |||
266 | return mtd->write_oob(mtd, offs, &ops); | ||
267 | } | ||
268 | |||
233 | /** | 269 | /** |
234 | * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page | 270 | * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page |
235 | * @mtd: MTD device structure | 271 | * @mtd: MTD device structure |
@@ -241,27 +277,85 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc | |||
241 | * We assume that the bbt bits are in consecutive order. | 277 | * We assume that the bbt bits are in consecutive order. |
242 | * | 278 | * |
243 | */ | 279 | */ |
244 | static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) | 280 | static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, |
281 | struct nand_bbt_descr *td, struct nand_bbt_descr *md) | ||
245 | { | 282 | { |
246 | struct nand_chip *this = mtd->priv; | 283 | struct nand_chip *this = mtd->priv; |
247 | 284 | ||
248 | /* Read the primary version, if available */ | 285 | /* Read the primary version, if available */ |
249 | if (td->options & NAND_BBT_VERSION) { | 286 | if (td->options & NAND_BBT_VERSION) { |
250 | nand_read_raw(mtd, buf, td->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize); | 287 | scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, |
288 | mtd->writesize); | ||
251 | td->version[0] = buf[mtd->writesize + td->veroffs]; | 289 | td->version[0] = buf[mtd->writesize + td->veroffs]; |
252 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); | 290 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", |
291 | td->pages[0], td->version[0]); | ||
253 | } | 292 | } |
254 | 293 | ||
255 | /* Read the mirror version, if available */ | 294 | /* Read the mirror version, if available */ |
256 | if (md && (md->options & NAND_BBT_VERSION)) { | 295 | if (md && (md->options & NAND_BBT_VERSION)) { |
257 | nand_read_raw(mtd, buf, md->pages[0] << this->page_shift, mtd->writesize, mtd->oobsize); | 296 | scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, |
297 | mtd->writesize); | ||
258 | md->version[0] = buf[mtd->writesize + md->veroffs]; | 298 | md->version[0] = buf[mtd->writesize + md->veroffs]; |
259 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); | 299 | printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", |
300 | md->pages[0], md->version[0]); | ||
260 | } | 301 | } |
261 | |||
262 | return 1; | 302 | return 1; |
263 | } | 303 | } |
264 | 304 | ||
305 | /* | ||
306 | * Scan a given block full | ||
307 | */ | ||
308 | static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, | ||
309 | loff_t offs, uint8_t *buf, size_t readlen, | ||
310 | int scanlen, int len) | ||
311 | { | ||
312 | int ret, j; | ||
313 | |||
314 | ret = scan_read_raw(mtd, buf, offs, readlen); | ||
315 | if (ret) | ||
316 | return ret; | ||
317 | |||
318 | for (j = 0; j < len; j++, buf += scanlen) { | ||
319 | if (check_pattern(buf, scanlen, mtd->writesize, bd)) | ||
320 | return 1; | ||
321 | } | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | /* | ||
326 | * Scan a given block partially | ||
327 | */ | ||
328 | static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, | ||
329 | loff_t offs, uint8_t *buf, int len) | ||
330 | { | ||
331 | struct mtd_oob_ops ops; | ||
332 | int j, ret; | ||
333 | |||
334 | ops.len = mtd->oobsize; | ||
335 | ops.ooblen = mtd->oobsize; | ||
336 | ops.oobbuf = buf; | ||
337 | ops.ooboffs = 0; | ||
338 | ops.datbuf = NULL; | ||
339 | ops.mode = MTD_OOB_PLACE; | ||
340 | |||
341 | for (j = 0; j < len; j++) { | ||
342 | /* | ||
343 | * Read the full oob until read_oob is fixed to | ||
344 | * handle single byte reads for 16 bit | ||
345 | * buswidth | ||
346 | */ | ||
347 | ret = mtd->read_oob(mtd, offs, &ops); | ||
348 | if (ret) | ||
349 | return ret; | ||
350 | |||
351 | if (check_short_pattern(buf, bd)) | ||
352 | return 1; | ||
353 | |||
354 | offs += mtd->writesize; | ||
355 | } | ||
356 | return 0; | ||
357 | } | ||
358 | |||
265 | /** | 359 | /** |
266 | * create_bbt - [GENERIC] Create a bad block table by scanning the device | 360 | * create_bbt - [GENERIC] Create a bad block table by scanning the device |
267 | * @mtd: MTD device structure | 361 | * @mtd: MTD device structure |
@@ -273,13 +367,14 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des | |||
273 | * Create a bad block table by scanning the device | 367 | * Create a bad block table by scanning the device |
274 | * for the given good/bad block identify pattern | 368 | * for the given good/bad block identify pattern |
275 | */ | 369 | */ |
276 | static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) | 370 | static int create_bbt(struct mtd_info *mtd, uint8_t *buf, |
371 | struct nand_bbt_descr *bd, int chip) | ||
277 | { | 372 | { |
278 | struct nand_chip *this = mtd->priv; | 373 | struct nand_chip *this = mtd->priv; |
279 | int i, j, numblocks, len, scanlen; | 374 | int i, numblocks, len, scanlen; |
280 | int startblock; | 375 | int startblock; |
281 | loff_t from; | 376 | loff_t from; |
282 | size_t readlen, ooblen; | 377 | size_t readlen; |
283 | 378 | ||
284 | printk(KERN_INFO "Scanning device for bad blocks\n"); | 379 | printk(KERN_INFO "Scanning device for bad blocks\n"); |
285 | 380 | ||
@@ -294,18 +389,17 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
294 | 389 | ||
295 | if (!(bd->options & NAND_BBT_SCANEMPTY)) { | 390 | if (!(bd->options & NAND_BBT_SCANEMPTY)) { |
296 | /* We need only read few bytes from the OOB area */ | 391 | /* We need only read few bytes from the OOB area */ |
297 | scanlen = ooblen = 0; | 392 | scanlen = 0; |
298 | readlen = bd->len; | 393 | readlen = bd->len; |
299 | } else { | 394 | } else { |
300 | /* Full page content should be read */ | 395 | /* Full page content should be read */ |
301 | scanlen = mtd->writesize + mtd->oobsize; | 396 | scanlen = mtd->writesize + mtd->oobsize; |
302 | readlen = len * mtd->writesize; | 397 | readlen = len * mtd->writesize; |
303 | ooblen = len * mtd->oobsize; | ||
304 | } | 398 | } |
305 | 399 | ||
306 | if (chip == -1) { | 400 | if (chip == -1) { |
307 | /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it | 401 | /* Note that numblocks is 2 * (real numblocks) here, see i+=2 |
308 | * makes shifting and masking less painful */ | 402 | * below as it makes shifting and masking less painful */ |
309 | numblocks = mtd->size >> (this->bbt_erase_shift - 1); | 403 | numblocks = mtd->size >> (this->bbt_erase_shift - 1); |
310 | startblock = 0; | 404 | startblock = 0; |
311 | from = 0; | 405 | from = 0; |
@@ -324,35 +418,21 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
324 | for (i = startblock; i < numblocks;) { | 418 | for (i = startblock; i < numblocks;) { |
325 | int ret; | 419 | int ret; |
326 | 420 | ||
327 | if (bd->options & NAND_BBT_SCANEMPTY) | 421 | if (bd->options & NAND_BBT_SCANALLPAGES) |
328 | if ((ret = nand_read_raw(mtd, buf, from, readlen, ooblen))) | 422 | ret = scan_block_full(mtd, bd, from, buf, readlen, |
329 | return ret; | 423 | scanlen, len); |
330 | 424 | else | |
331 | for (j = 0; j < len; j++) { | 425 | ret = scan_block_fast(mtd, bd, from, buf, len); |
332 | if (!(bd->options & NAND_BBT_SCANEMPTY)) { | 426 | |
333 | size_t retlen; | 427 | if (ret < 0) |
334 | 428 | return ret; | |
335 | /* Read the full oob until read_oob is fixed to | 429 | |
336 | * handle single byte reads for 16 bit buswidth */ | 430 | if (ret) { |
337 | ret = mtd->read_oob(mtd, from + j * mtd->writesize, mtd->oobsize, &retlen, buf); | 431 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); |
338 | if (ret) | 432 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", |
339 | return ret; | 433 | i >> 1, (unsigned int)from); |
340 | |||
341 | if (check_short_pattern(buf, bd)) { | ||
342 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); | ||
343 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | ||
344 | i >> 1, (unsigned int)from); | ||
345 | break; | ||
346 | } | ||
347 | } else { | ||
348 | if (check_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { | ||
349 | this->bbt[i >> 3] |= 0x03 << (i & 0x6); | ||
350 | printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", | ||
351 | i >> 1, (unsigned int)from); | ||
352 | break; | ||
353 | } | ||
354 | } | ||
355 | } | 434 | } |
435 | |||
356 | i += 2; | 436 | i += 2; |
357 | from += (1 << this->bbt_erase_shift); | 437 | from += (1 << this->bbt_erase_shift); |
358 | } | 438 | } |
@@ -383,6 +463,7 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
383 | int bits, startblock, block, dir; | 463 | int bits, startblock, block, dir; |
384 | int scanlen = mtd->writesize + mtd->oobsize; | 464 | int scanlen = mtd->writesize + mtd->oobsize; |
385 | int bbtblocks; | 465 | int bbtblocks; |
466 | int blocktopage = this->bbt_erase_shift - this->page_shift; | ||
386 | 467 | ||
387 | /* Search direction top -> down ? */ | 468 | /* Search direction top -> down ? */ |
388 | if (td->options & NAND_BBT_LASTBLOCK) { | 469 | if (td->options & NAND_BBT_LASTBLOCK) { |
@@ -412,11 +493,14 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
412 | td->pages[i] = -1; | 493 | td->pages[i] = -1; |
413 | /* Scan the maximum number of blocks */ | 494 | /* Scan the maximum number of blocks */ |
414 | for (block = 0; block < td->maxblocks; block++) { | 495 | for (block = 0; block < td->maxblocks; block++) { |
496 | |||
415 | int actblock = startblock + dir * block; | 497 | int actblock = startblock + dir * block; |
498 | loff_t offs = actblock << this->bbt_erase_shift; | ||
499 | |||
416 | /* Read first page */ | 500 | /* Read first page */ |
417 | nand_read_raw(mtd, buf, actblock << this->bbt_erase_shift, mtd->writesize, mtd->oobsize); | 501 | scan_read_raw(mtd, buf, offs, mtd->writesize); |
418 | if (!check_pattern(buf, scanlen, mtd->writesize, td)) { | 502 | if (!check_pattern(buf, scanlen, mtd->writesize, td)) { |
419 | td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); | 503 | td->pages[i] = actblock << blocktopage; |
420 | if (td->options & NAND_BBT_VERSION) { | 504 | if (td->options & NAND_BBT_VERSION) { |
421 | td->version[i] = buf[mtd->writesize + td->veroffs]; | 505 | td->version[i] = buf[mtd->writesize + td->veroffs]; |
422 | } | 506 | } |
@@ -481,8 +565,14 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
481 | int nrchips, bbtoffs, pageoffs, ooboffs; | 565 | int nrchips, bbtoffs, pageoffs, ooboffs; |
482 | uint8_t msk[4]; | 566 | uint8_t msk[4]; |
483 | uint8_t rcode = td->reserved_block_code; | 567 | uint8_t rcode = td->reserved_block_code; |
484 | size_t retlen, len = 0, ooblen; | 568 | size_t retlen, len = 0; |
485 | loff_t to; | 569 | loff_t to; |
570 | struct mtd_oob_ops ops; | ||
571 | |||
572 | ops.ooblen = mtd->oobsize; | ||
573 | ops.ooboffs = 0; | ||
574 | ops.datbuf = NULL; | ||
575 | ops.mode = MTD_OOB_PLACE; | ||
486 | 576 | ||
487 | if (!rcode) | 577 | if (!rcode) |
488 | rcode = 0xff; | 578 | rcode = 0xff; |
@@ -583,10 +673,10 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
583 | "bad block table\n"); | 673 | "bad block table\n"); |
584 | } | 674 | } |
585 | /* Read oob data */ | 675 | /* Read oob data */ |
586 | ooblen = (len >> this->page_shift) * mtd->oobsize; | 676 | ops.len = (len >> this->page_shift) * mtd->oobsize; |
587 | res = mtd->read_oob(mtd, to + mtd->writesize, ooblen, | 677 | ops.oobbuf = &buf[len]; |
588 | &retlen, &buf[len]); | 678 | res = mtd->read_oob(mtd, to + mtd->writesize, &ops); |
589 | if (res < 0 || retlen != ooblen) | 679 | if (res < 0 || ops.retlen != ops.len) |
590 | goto outerr; | 680 | goto outerr; |
591 | 681 | ||
592 | /* Calc the byte offset in the buffer */ | 682 | /* Calc the byte offset in the buffer */ |
@@ -635,7 +725,7 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, | |||
635 | if (res < 0) | 725 | if (res < 0) |
636 | goto outerr; | 726 | goto outerr; |
637 | 727 | ||
638 | res = nand_write_raw(mtd, to, len, &retlen, buf, &buf[len]); | 728 | res = scan_write_bbt(mtd, to, len, buf, &buf[len]); |
639 | if (res < 0) | 729 | if (res < 0) |
640 | goto outerr; | 730 | goto outerr; |
641 | 731 | ||
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 359533b33d9b..f6ffe7949b26 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c | |||
@@ -134,6 +134,69 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev) | |||
134 | kfree(nftl); | 134 | kfree(nftl); |
135 | } | 135 | } |
136 | 136 | ||
137 | /* | ||
138 | * Read oob data from flash | ||
139 | */ | ||
140 | int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, | ||
141 | size_t *retlen, uint8_t *buf) | ||
142 | { | ||
143 | struct mtd_oob_ops ops; | ||
144 | int res; | ||
145 | |||
146 | ops.mode = MTD_OOB_PLACE; | ||
147 | ops.ooboffs = offs & (mtd->writesize - 1); | ||
148 | ops.ooblen = len; | ||
149 | ops.oobbuf = buf; | ||
150 | ops.datbuf = NULL; | ||
151 | ops.len = len; | ||
152 | |||
153 | res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops); | ||
154 | *retlen = ops.retlen; | ||
155 | return res; | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Write oob data to flash | ||
160 | */ | ||
161 | int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, | ||
162 | size_t *retlen, uint8_t *buf) | ||
163 | { | ||
164 | struct mtd_oob_ops ops; | ||
165 | int res; | ||
166 | |||
167 | ops.mode = MTD_OOB_PLACE; | ||
168 | ops.ooboffs = offs & (mtd->writesize - 1); | ||
169 | ops.ooblen = len; | ||
170 | ops.oobbuf = buf; | ||
171 | ops.datbuf = NULL; | ||
172 | ops.len = len; | ||
173 | |||
174 | res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); | ||
175 | *retlen = ops.retlen; | ||
176 | return res; | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * Write data and oob to flash | ||
181 | */ | ||
182 | static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len, | ||
183 | size_t *retlen, uint8_t *buf, uint8_t *oob) | ||
184 | { | ||
185 | struct mtd_oob_ops ops; | ||
186 | int res; | ||
187 | |||
188 | ops.mode = MTD_OOB_PLACE; | ||
189 | ops.ooboffs = offs; | ||
190 | ops.ooblen = mtd->oobsize; | ||
191 | ops.oobbuf = oob; | ||
192 | ops.datbuf = buf; | ||
193 | ops.len = len; | ||
194 | |||
195 | res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops); | ||
196 | *retlen = ops.retlen; | ||
197 | return res; | ||
198 | } | ||
199 | |||
137 | #ifdef CONFIG_NFTL_RW | 200 | #ifdef CONFIG_NFTL_RW |
138 | 201 | ||
139 | /* Actual NFTL access routines */ | 202 | /* Actual NFTL access routines */ |
@@ -216,7 +279,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p | |||
216 | 279 | ||
217 | targetEUN = thisEUN; | 280 | targetEUN = thisEUN; |
218 | for (block = 0; block < nftl->EraseSize / 512; block ++) { | 281 | for (block = 0; block < nftl->EraseSize / 512; block ++) { |
219 | mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) + | 282 | nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + |
220 | (block * 512), 16 , &retlen, | 283 | (block * 512), 16 , &retlen, |
221 | (char *)&oob); | 284 | (char *)&oob); |
222 | if (block == 2) { | 285 | if (block == 2) { |
@@ -333,7 +396,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p | |||
333 | longer one */ | 396 | longer one */ |
334 | oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); | 397 | oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); |
335 | oob.u.c.unused = 0xffffffff; | 398 | oob.u.c.unused = 0xffffffff; |
336 | mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, | 399 | nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, |
337 | 8, &retlen, (char *)&oob.u); | 400 | 8, &retlen, (char *)&oob.u); |
338 | } | 401 | } |
339 | 402 | ||
@@ -369,17 +432,15 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p | |||
369 | memset(&oob, 0xff, sizeof(struct nftl_oob)); | 432 | memset(&oob, 0xff, sizeof(struct nftl_oob)); |
370 | oob.b.Status = oob.b.Status1 = SECTOR_USED; | 433 | oob.b.Status = oob.b.Status1 = SECTOR_USED; |
371 | 434 | ||
372 | nand_write_raw(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + | 435 | nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + |
373 | (block * 512), 512, &retlen, movebuf, | 436 | (block * 512), 512, &retlen, movebuf, (char *)&oob); |
374 | (char *)&oob); | ||
375 | |||
376 | } | 437 | } |
377 | 438 | ||
378 | /* add the header so that it is now a valid chain */ | 439 | /* add the header so that it is now a valid chain */ |
379 | oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); | 440 | oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); |
380 | oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; | 441 | oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; |
381 | 442 | ||
382 | mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, | 443 | nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8, |
383 | 8, &retlen, (char *)&oob.u); | 444 | 8, &retlen, (char *)&oob.u); |
384 | 445 | ||
385 | /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ | 446 | /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ |
@@ -499,7 +560,7 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) | |||
499 | 560 | ||
500 | lastEUN = writeEUN; | 561 | lastEUN = writeEUN; |
501 | 562 | ||
502 | mtd->read_oob(mtd, | 563 | nftl_read_oob(mtd, |
503 | (writeEUN * nftl->EraseSize) + blockofs, | 564 | (writeEUN * nftl->EraseSize) + blockofs, |
504 | 8, &retlen, (char *)&bci); | 565 | 8, &retlen, (char *)&bci); |
505 | 566 | ||
@@ -588,12 +649,12 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) | |||
588 | nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; | 649 | nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; |
589 | 650 | ||
590 | /* ... and on the flash itself */ | 651 | /* ... and on the flash itself */ |
591 | mtd->read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, | 652 | nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, |
592 | &retlen, (char *)&oob.u); | 653 | &retlen, (char *)&oob.u); |
593 | 654 | ||
594 | oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); | 655 | oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); |
595 | 656 | ||
596 | mtd->write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, | 657 | nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8, |
597 | &retlen, (char *)&oob.u); | 658 | &retlen, (char *)&oob.u); |
598 | 659 | ||
599 | /* we link the new block to the chain only after the | 660 | /* we link the new block to the chain only after the |
@@ -603,13 +664,13 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) | |||
603 | /* Both in our cache... */ | 664 | /* Both in our cache... */ |
604 | nftl->ReplUnitTable[lastEUN] = writeEUN; | 665 | nftl->ReplUnitTable[lastEUN] = writeEUN; |
605 | /* ... and on the flash itself */ | 666 | /* ... and on the flash itself */ |
606 | mtd->read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, | 667 | nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8, |
607 | 8, &retlen, (char *)&oob.u); | 668 | 8, &retlen, (char *)&oob.u); |
608 | 669 | ||
609 | oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum | 670 | oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum |
610 | = cpu_to_le16(writeEUN); | 671 | = cpu_to_le16(writeEUN); |
611 | 672 | ||
612 | mtd->write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, | 673 | nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8, |
613 | 8, &retlen, (char *)&oob.u); | 674 | 8, &retlen, (char *)&oob.u); |
614 | } | 675 | } |
615 | 676 | ||
@@ -643,9 +704,8 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, | |||
643 | memset(&oob, 0xff, sizeof(struct nftl_oob)); | 704 | memset(&oob, 0xff, sizeof(struct nftl_oob)); |
644 | oob.b.Status = oob.b.Status1 = SECTOR_USED; | 705 | oob.b.Status = oob.b.Status1 = SECTOR_USED; |
645 | 706 | ||
646 | nand_write_raw(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + | 707 | nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, |
647 | blockofs, 512, &retlen, (char *)buffer, | 708 | 512, &retlen, (char *)buffer, (char *)&oob); |
648 | (char *)&oob); | ||
649 | return 0; | 709 | return 0; |
650 | } | 710 | } |
651 | #endif /* CONFIG_NFTL_RW */ | 711 | #endif /* CONFIG_NFTL_RW */ |
@@ -667,7 +727,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, | |||
667 | 727 | ||
668 | if (thisEUN != BLOCK_NIL) { | 728 | if (thisEUN != BLOCK_NIL) { |
669 | while (thisEUN < nftl->nb_blocks) { | 729 | while (thisEUN < nftl->nb_blocks) { |
670 | if (mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) + | 730 | if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) + |
671 | blockofs, 8, &retlen, | 731 | blockofs, 8, &retlen, |
672 | (char *)&bci) < 0) | 732 | (char *)&bci) < 0) |
673 | status = SECTOR_IGNORE; | 733 | status = SECTOR_IGNORE; |
diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 521b07cd2326..067262ee8df0 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c | |||
@@ -33,6 +33,11 @@ | |||
33 | 33 | ||
34 | char nftlmountrev[]="$Revision: 1.41 $"; | 34 | char nftlmountrev[]="$Revision: 1.41 $"; |
35 | 35 | ||
36 | extern int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len, | ||
37 | size_t *retlen, uint8_t *buf); | ||
38 | extern int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len, | ||
39 | size_t *retlen, uint8_t *buf); | ||
40 | |||
36 | /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the | 41 | /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the |
37 | * various device information of the NFTL partition and Bad Unit Table. Update | 42 | * various device information of the NFTL partition and Bad Unit Table. Update |
38 | * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] | 43 | * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[] |
@@ -92,7 +97,7 @@ static int find_boot_record(struct NFTLrecord *nftl) | |||
92 | } | 97 | } |
93 | 98 | ||
94 | /* To be safer with BIOS, also use erase mark as discriminant */ | 99 | /* To be safer with BIOS, also use erase mark as discriminant */ |
95 | if ((ret = mtd->read_oob(mtd, block * nftl->EraseSize + | 100 | if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize + |
96 | SECTORSIZE + 8, 8, &retlen, | 101 | SECTORSIZE + 8, 8, &retlen, |
97 | (char *)&h1) < 0)) { | 102 | (char *)&h1) < 0)) { |
98 | printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", | 103 | printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", |
@@ -283,7 +288,7 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int | |||
283 | return -1; | 288 | return -1; |
284 | 289 | ||
285 | if (check_oob) { | 290 | if (check_oob) { |
286 | if(mtd->read_oob(mtd, address, mtd->oobsize, | 291 | if(nftl_read_oob(mtd, address, mtd->oobsize, |
287 | &retlen, &buf[SECTORSIZE]) < 0) | 292 | &retlen, &buf[SECTORSIZE]) < 0) |
288 | return -1; | 293 | return -1; |
289 | if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) | 294 | if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0) |
@@ -311,7 +316,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) | |||
311 | struct mtd_info *mtd = nftl->mbd.mtd; | 316 | struct mtd_info *mtd = nftl->mbd.mtd; |
312 | 317 | ||
313 | /* Read the Unit Control Information #1 for Wear-Leveling */ | 318 | /* Read the Unit Control Information #1 for Wear-Leveling */ |
314 | if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, | 319 | if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, |
315 | 8, &retlen, (char *)&uci) < 0) | 320 | 8, &retlen, (char *)&uci) < 0) |
316 | goto default_uci1; | 321 | goto default_uci1; |
317 | 322 | ||
@@ -351,7 +356,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) | |||
351 | goto fail; | 356 | goto fail; |
352 | 357 | ||
353 | uci.WearInfo = le32_to_cpu(nb_erases); | 358 | uci.WearInfo = le32_to_cpu(nb_erases); |
354 | if (mtd->write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + | 359 | if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE + |
355 | 8, 8, &retlen, (char *)&uci) < 0) | 360 | 8, 8, &retlen, (char *)&uci) < 0) |
356 | goto fail; | 361 | goto fail; |
357 | return 0; | 362 | return 0; |
@@ -383,7 +388,7 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b | |||
383 | block = first_block; | 388 | block = first_block; |
384 | for (;;) { | 389 | for (;;) { |
385 | for (i = 0; i < sectors_per_block; i++) { | 390 | for (i = 0; i < sectors_per_block; i++) { |
386 | if (mtd->read_oob(mtd, | 391 | if (nftl_read_oob(mtd, |
387 | block * nftl->EraseSize + i * SECTORSIZE, | 392 | block * nftl->EraseSize + i * SECTORSIZE, |
388 | 8, &retlen, (char *)&bci) < 0) | 393 | 8, &retlen, (char *)&bci) < 0) |
389 | status = SECTOR_IGNORE; | 394 | status = SECTOR_IGNORE; |
@@ -404,7 +409,7 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b | |||
404 | /* sector not free actually : mark it as SECTOR_IGNORE */ | 409 | /* sector not free actually : mark it as SECTOR_IGNORE */ |
405 | bci.Status = SECTOR_IGNORE; | 410 | bci.Status = SECTOR_IGNORE; |
406 | bci.Status1 = SECTOR_IGNORE; | 411 | bci.Status1 = SECTOR_IGNORE; |
407 | mtd->write_oob(mtd, block * | 412 | nftl_write_oob(mtd, block * |
408 | nftl->EraseSize + | 413 | nftl->EraseSize + |
409 | i * SECTORSIZE, 8, | 414 | i * SECTORSIZE, 8, |
410 | &retlen, (char *)&bci); | 415 | &retlen, (char *)&bci); |
@@ -498,7 +503,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) | |||
498 | size_t retlen; | 503 | size_t retlen; |
499 | 504 | ||
500 | /* check erase mark. */ | 505 | /* check erase mark. */ |
501 | if (mtd->read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, | 506 | if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, |
502 | &retlen, (char *)&h1) < 0) | 507 | &retlen, (char *)&h1) < 0) |
503 | return -1; | 508 | return -1; |
504 | 509 | ||
@@ -513,7 +518,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) | |||
513 | h1.EraseMark = cpu_to_le16(ERASE_MARK); | 518 | h1.EraseMark = cpu_to_le16(ERASE_MARK); |
514 | h1.EraseMark1 = cpu_to_le16(ERASE_MARK); | 519 | h1.EraseMark1 = cpu_to_le16(ERASE_MARK); |
515 | h1.WearInfo = cpu_to_le32(0); | 520 | h1.WearInfo = cpu_to_le32(0); |
516 | if (mtd->write_oob(mtd, | 521 | if (nftl_write_oob(mtd, |
517 | block * nftl->EraseSize + SECTORSIZE + 8, 8, | 522 | block * nftl->EraseSize + SECTORSIZE + 8, 8, |
518 | &retlen, (char *)&h1) < 0) | 523 | &retlen, (char *)&h1) < 0) |
519 | return -1; | 524 | return -1; |
@@ -526,7 +531,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) | |||
526 | SECTORSIZE, 0) != 0) | 531 | SECTORSIZE, 0) != 0) |
527 | return -1; | 532 | return -1; |
528 | 533 | ||
529 | if (mtd->read_oob(mtd, block * nftl->EraseSize + i, | 534 | if (nftl_read_oob(mtd, block * nftl->EraseSize + i, |
530 | 16, &retlen, buf) < 0) | 535 | 16, &retlen, buf) < 0) |
531 | return -1; | 536 | return -1; |
532 | if (i == SECTORSIZE) { | 537 | if (i == SECTORSIZE) { |
@@ -557,7 +562,7 @@ static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block) | |||
557 | struct nftl_uci2 uci; | 562 | struct nftl_uci2 uci; |
558 | size_t retlen; | 563 | size_t retlen; |
559 | 564 | ||
560 | if (mtd->read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, | 565 | if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, |
561 | 8, &retlen, (char *)&uci) < 0) | 566 | 8, &retlen, (char *)&uci) < 0) |
562 | return 0; | 567 | return 0; |
563 | 568 | ||
@@ -597,10 +602,10 @@ int NFTL_mount(struct NFTLrecord *s) | |||
597 | 602 | ||
598 | for (;;) { | 603 | for (;;) { |
599 | /* read the block header. If error, we format the chain */ | 604 | /* read the block header. If error, we format the chain */ |
600 | if (mtd->read_oob(mtd, | 605 | if (nftl_read_oob(mtd, |
601 | block * s->EraseSize + 8, 8, | 606 | block * s->EraseSize + 8, 8, |
602 | &retlen, (char *)&h0) < 0 || | 607 | &retlen, (char *)&h0) < 0 || |
603 | mtd->read_oob(mtd, | 608 | nftl_read_oob(mtd, |
604 | block * s->EraseSize + | 609 | block * s->EraseSize + |
605 | SECTORSIZE + 8, 8, | 610 | SECTORSIZE + 8, 8, |
606 | &retlen, (char *)&h1) < 0) { | 611 | &retlen, (char *)&h1) < 0) { |
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index a0d3f011c0f2..84ec40d25438 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -671,7 +671,7 @@ out: | |||
671 | } | 671 | } |
672 | 672 | ||
673 | /** | 673 | /** |
674 | * onenand_read_oob - [MTD Interface] OneNAND read out-of-band | 674 | * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band |
675 | * @param mtd MTD device structure | 675 | * @param mtd MTD device structure |
676 | * @param from offset to read from | 676 | * @param from offset to read from |
677 | * @param len number of bytes to read | 677 | * @param len number of bytes to read |
@@ -680,8 +680,8 @@ out: | |||
680 | * | 680 | * |
681 | * OneNAND read out-of-band data from the spare area | 681 | * OneNAND read out-of-band data from the spare area |
682 | */ | 682 | */ |
683 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 683 | int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, |
684 | size_t *retlen, u_char *buf) | 684 | size_t *retlen, u_char *buf) |
685 | { | 685 | { |
686 | struct onenand_chip *this = mtd->priv; | 686 | struct onenand_chip *this = mtd->priv; |
687 | int read = 0, thislen, column; | 687 | int read = 0, thislen, column; |
@@ -744,6 +744,21 @@ out: | |||
744 | return ret; | 744 | return ret; |
745 | } | 745 | } |
746 | 746 | ||
747 | /** | ||
748 | * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band | ||
749 | * @mtd: MTD device structure | ||
750 | * @from: offset to read from | ||
751 | * @ops: oob operation description structure | ||
752 | */ | ||
753 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | ||
754 | struct mtd_oob_ops *ops) | ||
755 | { | ||
756 | BUG_ON(ops->mode != MTD_OOB_PLACE); | ||
757 | |||
758 | return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->len, | ||
759 | &ops->retlen, ops->oobbuf); | ||
760 | } | ||
761 | |||
747 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE | 762 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE |
748 | /** | 763 | /** |
749 | * onenand_verify_oob - [GENERIC] verify the oob contents after a write | 764 | * onenand_verify_oob - [GENERIC] verify the oob contents after a write |
@@ -894,7 +909,7 @@ out: | |||
894 | } | 909 | } |
895 | 910 | ||
896 | /** | 911 | /** |
897 | * onenand_write_oob - [MTD Interface] OneNAND write out-of-band | 912 | * onenand_do_write_oob - [Internal] OneNAND write out-of-band |
898 | * @param mtd MTD device structure | 913 | * @param mtd MTD device structure |
899 | * @param to offset to write to | 914 | * @param to offset to write to |
900 | * @param len number of bytes to write | 915 | * @param len number of bytes to write |
@@ -903,8 +918,8 @@ out: | |||
903 | * | 918 | * |
904 | * OneNAND write out-of-band | 919 | * OneNAND write out-of-band |
905 | */ | 920 | */ |
906 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | 921 | static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, |
907 | size_t *retlen, const u_char *buf) | 922 | size_t *retlen, const u_char *buf) |
908 | { | 923 | { |
909 | struct onenand_chip *this = mtd->priv; | 924 | struct onenand_chip *this = mtd->priv; |
910 | int column, ret = 0; | 925 | int column, ret = 0; |
@@ -973,6 +988,21 @@ out: | |||
973 | } | 988 | } |
974 | 989 | ||
975 | /** | 990 | /** |
991 | * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band | ||
992 | * @mtd: MTD device structure | ||
993 | * @from: offset to read from | ||
994 | * @ops: oob operation description structure | ||
995 | */ | ||
996 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, | ||
997 | struct mtd_oob_ops *ops) | ||
998 | { | ||
999 | BUG_ON(ops->mode != MTD_OOB_PLACE); | ||
1000 | |||
1001 | return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->len, | ||
1002 | &ops->retlen, ops->oobbuf); | ||
1003 | } | ||
1004 | |||
1005 | /** | ||
976 | * onenand_block_checkbad - [GENERIC] Check if a block is marked bad | 1006 | * onenand_block_checkbad - [GENERIC] Check if a block is marked bad |
977 | * @param mtd MTD device structure | 1007 | * @param mtd MTD device structure |
978 | * @param ofs offset from device start | 1008 | * @param ofs offset from device start |
@@ -1138,7 +1168,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1138 | 1168 | ||
1139 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 1169 | /* We write two bytes, so we dont have to mess with 16 bit access */ |
1140 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | 1170 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); |
1141 | return mtd->write_oob(mtd, ofs , 2, &retlen, buf); | 1171 | return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); |
1142 | } | 1172 | } |
1143 | 1173 | ||
1144 | /** | 1174 | /** |
@@ -1328,7 +1358,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, | |||
1328 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); | 1358 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
1329 | this->wait(mtd, FL_OTPING); | 1359 | this->wait(mtd, FL_OTPING); |
1330 | 1360 | ||
1331 | ret = mtd->write_oob(mtd, from, len, retlen, buf); | 1361 | ret = onenand_do_write_oob(mtd, from, len, retlen, buf); |
1332 | 1362 | ||
1333 | /* Exit OTP access mode */ | 1363 | /* Exit OTP access mode */ |
1334 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 1364 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index aafd7c2f7802..1b00dac3d7d6 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c | |||
@@ -17,6 +17,9 @@ | |||
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, | ||
21 | size_t *retlen, u_char *buf); | ||
22 | |||
20 | /** | 23 | /** |
21 | * 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 |
22 | * @param buf the buffer to search | 25 | * @param buf the buffer to search |
@@ -87,8 +90,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr | |||
87 | 90 | ||
88 | /* No need to read pages fully, | 91 | /* No need to read pages fully, |
89 | * just read required OOB bytes */ | 92 | * just read required OOB bytes */ |
90 | ret = mtd->read_oob(mtd, from + j * mtd->writesize + bd->offs, | 93 | ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, |
91 | readlen, &retlen, &buf[0]); | 94 | readlen, &retlen, &buf[0]); |
92 | 95 | ||
93 | if (ret) | 96 | if (ret) |
94 | return ret; | 97 | return ret; |
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 506690cc9a78..935fec1b1201 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h | |||
@@ -100,6 +100,7 @@ struct jffs2_sb_info { | |||
100 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER | 100 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER |
101 | /* Write-behind buffer for NAND flash */ | 101 | /* Write-behind buffer for NAND flash */ |
102 | unsigned char *wbuf; | 102 | unsigned char *wbuf; |
103 | unsigned char *oobbuf; | ||
103 | uint32_t wbuf_ofs; | 104 | uint32_t wbuf_ofs; |
104 | uint32_t wbuf_len; | 105 | uint32_t wbuf_len; |
105 | struct jffs2_inodirty *wbuf_inodes; | 106 | struct jffs2_inodirty *wbuf_inodes; |
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index c6a62e162963..1195d06d4373 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c | |||
@@ -955,158 +955,159 @@ exit: | |||
955 | return ret; | 955 | return ret; |
956 | } | 956 | } |
957 | 957 | ||
958 | #define NR_OOB_SCAN_PAGES 4 | ||
959 | |||
958 | /* | 960 | /* |
959 | * Check, if the out of band area is empty | 961 | * Check, if the out of band area is empty |
960 | */ | 962 | */ |
961 | int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) | 963 | int jffs2_check_oob_empty(struct jffs2_sb_info *c, |
964 | struct jffs2_eraseblock *jeb, int mode) | ||
962 | { | 965 | { |
963 | unsigned char *buf; | 966 | int i, page, ret; |
964 | int ret = 0; | 967 | int oobsize = c->mtd->oobsize; |
965 | int i,len,page; | 968 | struct mtd_oob_ops ops; |
966 | size_t retlen; | 969 | |
967 | int oob_size; | 970 | ops.len = NR_OOB_SCAN_PAGES * oobsize; |
968 | 971 | ops.ooblen = oobsize; | |
969 | /* allocate a buffer for all oob data in this sector */ | 972 | ops.oobbuf = c->oobbuf; |
970 | oob_size = c->mtd->oobsize; | 973 | ops.ooboffs = 0; |
971 | len = 4 * oob_size; | 974 | ops.datbuf = NULL; |
972 | buf = kmalloc(len, GFP_KERNEL); | 975 | ops.mode = MTD_OOB_PLACE; |
973 | if (!buf) { | 976 | |
974 | printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); | 977 | ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); |
975 | return -ENOMEM; | ||
976 | } | ||
977 | /* | ||
978 | * if mode = 0, we scan for a total empty oob area, else we have | ||
979 | * to take care of the cleanmarker in the first page of the block | ||
980 | */ | ||
981 | ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf); | ||
982 | if (ret) { | 978 | if (ret) { |
983 | D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); | 979 | D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " |
984 | goto out; | 980 | "failed %d for block at %08x\n", ret, jeb->offset)); |
981 | return ret; | ||
985 | } | 982 | } |
986 | 983 | ||
987 | if (retlen < len) { | 984 | if (ops.retlen < ops.len) { |
988 | D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " | 985 | D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " |
989 | "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); | 986 | "returned short read (%zd bytes not %d) for block " |
990 | ret = -EIO; | 987 | "at %08x\n", ops.retlen, ops.len, jeb->offset)); |
991 | goto out; | 988 | return -EIO; |
992 | } | 989 | } |
993 | 990 | ||
994 | /* Special check for first page */ | 991 | /* Special check for first page */ |
995 | for(i = 0; i < oob_size ; i++) { | 992 | for(i = 0; i < oobsize ; i++) { |
996 | /* Yeah, we know about the cleanmarker. */ | 993 | /* Yeah, we know about the cleanmarker. */ |
997 | if (mode && i >= c->fsdata_pos && | 994 | if (mode && i >= c->fsdata_pos && |
998 | i < c->fsdata_pos + c->fsdata_len) | 995 | i < c->fsdata_pos + c->fsdata_len) |
999 | continue; | 996 | continue; |
1000 | 997 | ||
1001 | if (buf[i] != 0xFF) { | 998 | if (ops.oobbuf[i] != 0xFF) { |
1002 | D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", | 999 | D2(printk(KERN_DEBUG "Found %02x at %x in OOB for " |
1003 | buf[i], i, jeb->offset)); | 1000 | "%08x\n", ops.oobbuf[i], i, jeb->offset)); |
1004 | ret = 1; | 1001 | return 1; |
1005 | goto out; | ||
1006 | } | 1002 | } |
1007 | } | 1003 | } |
1008 | 1004 | ||
1009 | /* we know, we are aligned :) */ | 1005 | /* we know, we are aligned :) */ |
1010 | for (page = oob_size; page < len; page += sizeof(long)) { | 1006 | for (page = oobsize; page < ops.len; page += sizeof(long)) { |
1011 | unsigned long dat = *(unsigned long *)(&buf[page]); | 1007 | long dat = *(long *)(&ops.oobbuf[page]); |
1012 | if(dat != -1) { | 1008 | if(dat != -1) |
1013 | ret = 1; | 1009 | return 1; |
1014 | goto out; | ||
1015 | } | ||
1016 | } | 1010 | } |
1017 | 1011 | return 0; | |
1018 | out: | ||
1019 | kfree(buf); | ||
1020 | |||
1021 | return ret; | ||
1022 | } | 1012 | } |
1023 | 1013 | ||
1024 | /* | 1014 | /* |
1025 | * Scan for a valid cleanmarker and for bad blocks | 1015 | * Scan for a valid cleanmarker and for bad blocks |
1026 | * For virtual blocks (concatenated physical blocks) check the cleanmarker | 1016 | */ |
1027 | * only in the first page of the first physical block, but scan for bad blocks in all | 1017 | int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, |
1028 | * physical blocks | 1018 | struct jffs2_eraseblock *jeb) |
1029 | */ | ||
1030 | int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) | ||
1031 | { | 1019 | { |
1032 | struct jffs2_unknown_node n; | 1020 | struct jffs2_unknown_node n; |
1033 | unsigned char buf[2 * NAND_MAX_OOBSIZE]; | 1021 | struct mtd_oob_ops ops; |
1034 | unsigned char *p; | 1022 | int oobsize = c->mtd->oobsize; |
1035 | int ret, i, cnt, retval = 0; | 1023 | unsigned char *p,*b; |
1036 | size_t retlen, offset; | 1024 | int i, ret; |
1037 | int oob_size; | 1025 | size_t offset = jeb->offset; |
1038 | 1026 | ||
1039 | offset = jeb->offset; | 1027 | /* Check first if the block is bad. */ |
1040 | oob_size = c->mtd->oobsize; | 1028 | if (c->mtd->block_isbad(c->mtd, offset)) { |
1041 | 1029 | D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()" | |
1042 | /* Loop through the physical blocks */ | 1030 | ": Bad block at %08x\n", jeb->offset)); |
1043 | for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { | 1031 | return 2; |
1044 | /* Check first if the block is bad. */ | 1032 | } |
1045 | if (c->mtd->block_isbad (c->mtd, offset)) { | ||
1046 | D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); | ||
1047 | return 2; | ||
1048 | } | ||
1049 | /* | ||
1050 | * We read oob data from page 0 and 1 of the block. | ||
1051 | * page 0 contains cleanmarker and badblock info | ||
1052 | * page 1 contains failure count of this block | ||
1053 | */ | ||
1054 | ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf); | ||
1055 | 1033 | ||
1056 | if (ret) { | 1034 | ops.len = oobsize; |
1057 | D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); | 1035 | ops.ooblen = oobsize; |
1058 | return ret; | 1036 | ops.oobbuf = c->oobbuf; |
1059 | } | 1037 | ops.ooboffs = 0; |
1060 | if (retlen < (oob_size << 1)) { | 1038 | ops.datbuf = NULL; |
1061 | D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset)); | 1039 | ops.mode = MTD_OOB_PLACE; |
1062 | return -EIO; | ||
1063 | } | ||
1064 | 1040 | ||
1065 | /* Check cleanmarker only on the first physical block */ | 1041 | ret = c->mtd->read_oob(c->mtd, offset, &ops); |
1066 | if (!cnt) { | 1042 | if (ret) { |
1067 | n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); | 1043 | D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " |
1068 | n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); | 1044 | "Read OOB failed %d for block at %08x\n", |
1069 | n.totlen = cpu_to_je32 (8); | 1045 | ret, jeb->offset)); |
1070 | p = (unsigned char *) &n; | 1046 | return ret; |
1047 | } | ||
1071 | 1048 | ||
1072 | for (i = 0; i < c->fsdata_len; i++) { | 1049 | if (ops.retlen < ops.len) { |
1073 | if (buf[c->fsdata_pos + i] != p[i]) { | 1050 | D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " |
1074 | retval = 1; | 1051 | "Read OOB return short read (%zd bytes not %d) " |
1075 | } | 1052 | "for block at %08x\n", ops.retlen, ops.len, |
1076 | } | 1053 | jeb->offset)); |
1077 | D1(if (retval == 1) { | 1054 | return -EIO; |
1078 | printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset); | ||
1079 | printk(KERN_WARNING "OOB at %08zx was ", offset); | ||
1080 | for (i=0; i < oob_size; i++) { | ||
1081 | printk("%02x ", buf[i]); | ||
1082 | } | ||
1083 | printk("\n"); | ||
1084 | }) | ||
1085 | } | ||
1086 | offset += c->mtd->erasesize; | ||
1087 | } | 1055 | } |
1088 | return retval; | 1056 | |
1057 | n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); | ||
1058 | n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); | ||
1059 | n.totlen = cpu_to_je32 (8); | ||
1060 | p = (unsigned char *) &n; | ||
1061 | b = c->oobbuf + c->fsdata_pos; | ||
1062 | |||
1063 | for (i = c->fsdata_len; i; i--) { | ||
1064 | if (*b++ != *p++) | ||
1065 | ret = 1; | ||
1066 | } | ||
1067 | |||
1068 | D1(if (ret == 1) { | ||
1069 | printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " | ||
1070 | "Cleanmarker node not detected in block at %08x\n", | ||
1071 | offset); | ||
1072 | printk(KERN_WARNING "OOB at %08zx was ", offset); | ||
1073 | for (i=0; i < oobsize; i++) | ||
1074 | printk("%02x ", c->oobbuf[i]); | ||
1075 | printk("\n"); | ||
1076 | }); | ||
1077 | return ret; | ||
1089 | } | 1078 | } |
1090 | 1079 | ||
1091 | int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) | 1080 | int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, |
1081 | struct jffs2_eraseblock *jeb) | ||
1092 | { | 1082 | { |
1093 | struct jffs2_unknown_node n; | 1083 | struct jffs2_unknown_node n; |
1094 | int ret; | 1084 | int ret; |
1095 | size_t retlen; | 1085 | struct mtd_oob_ops ops; |
1096 | 1086 | ||
1097 | n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); | 1087 | n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); |
1098 | n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); | 1088 | n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); |
1099 | n.totlen = cpu_to_je32(8); | 1089 | n.totlen = cpu_to_je32(8); |
1100 | 1090 | ||
1101 | ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); | 1091 | ops.len = c->fsdata_len; |
1092 | ops.ooblen = c->fsdata_len;; | ||
1093 | ops.oobbuf = (uint8_t *)&n; | ||
1094 | ops.ooboffs = c->fsdata_pos; | ||
1095 | ops.datbuf = NULL; | ||
1096 | ops.mode = MTD_OOB_PLACE; | ||
1097 | |||
1098 | ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); | ||
1102 | 1099 | ||
1103 | if (ret) { | 1100 | if (ret) { |
1104 | D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); | 1101 | D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " |
1102 | "Write failed for block at %08x: error %d\n", | ||
1103 | jeb->offset, ret)); | ||
1105 | return ret; | 1104 | return ret; |
1106 | } | 1105 | } |
1107 | if (retlen != c->fsdata_len) { | 1106 | if (ops.retlen != ops.len) { |
1108 | D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len)); | 1107 | D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " |
1109 | return ret; | 1108 | "Short write for block at %08x: %zd not %d\n", |
1109 | jeb->offset, ops.retlen, ops.len)); | ||
1110 | return -EIO; | ||
1110 | } | 1111 | } |
1111 | return 0; | 1112 | return 0; |
1112 | } | 1113 | } |
@@ -1185,6 +1186,10 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) | |||
1185 | if (!c->wbuf) | 1186 | if (!c->wbuf) |
1186 | return -ENOMEM; | 1187 | return -ENOMEM; |
1187 | 1188 | ||
1189 | c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL); | ||
1190 | if (!c->oobbuf) | ||
1191 | return -ENOMEM; | ||
1192 | |||
1188 | res = jffs2_nand_set_oobinfo(c); | 1193 | res = jffs2_nand_set_oobinfo(c); |
1189 | 1194 | ||
1190 | #ifdef BREAKME | 1195 | #ifdef BREAKME |
@@ -1202,6 +1207,7 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) | |||
1202 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) | 1207 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) |
1203 | { | 1208 | { |
1204 | kfree(c->wbuf); | 1209 | kfree(c->wbuf); |
1210 | kfree(c->oobbuf); | ||
1205 | } | 1211 | } |
1206 | 1212 | ||
1207 | int jffs2_dataflash_setup(struct jffs2_sb_info *c) { | 1213 | int jffs2_dataflash_setup(struct jffs2_sb_info *c) { |
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 4970c2e96fbf..e75bb584e80b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h | |||
@@ -67,6 +67,50 @@ struct mtd_ecc_stats { | |||
67 | unsigned long failed; | 67 | unsigned long failed; |
68 | }; | 68 | }; |
69 | 69 | ||
70 | /* | ||
71 | * oob operation modes | ||
72 | * | ||
73 | * MTD_OOB_PLACE: oob data are placed at the given offset | ||
74 | * MTD_OOB_AUTO: oob data are automatically placed at the free areas | ||
75 | * which are defined by the ecclayout | ||
76 | * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data | ||
77 | * is inserted into the data. Thats a raw image of the | ||
78 | * flash contents. | ||
79 | */ | ||
80 | typedef enum { | ||
81 | MTD_OOB_PLACE, | ||
82 | MTD_OOB_AUTO, | ||
83 | MTD_OOB_RAW, | ||
84 | } mtd_oob_mode_t; | ||
85 | |||
86 | /** | ||
87 | * struct mtd_oob_ops - oob operation operands | ||
88 | * @mode: operation mode | ||
89 | * | ||
90 | * @len: number of bytes to write/read. When a data buffer is given | ||
91 | * (datbuf != NULL) this is the number of data bytes. When | ||
92 | + no data buffer is available this is the number of oob bytes. | ||
93 | * | ||
94 | * @retlen: number of bytes written/read. When a data buffer is given | ||
95 | * (datbuf != NULL) this is the number of data bytes. When | ||
96 | + no data buffer is available this is the number of oob bytes. | ||
97 | * | ||
98 | * @ooblen: number of oob bytes per page | ||
99 | * @ooboffs: offset of oob data in the oob area (only relevant when | ||
100 | * mode = MTD_OOB_PLACE) | ||
101 | * @datbuf: data buffer - if NULL only oob data are read/written | ||
102 | * @oobbuf: oob data buffer | ||
103 | */ | ||
104 | struct mtd_oob_ops { | ||
105 | mtd_oob_mode_t mode; | ||
106 | size_t len; | ||
107 | size_t retlen; | ||
108 | size_t ooblen; | ||
109 | uint32_t ooboffs; | ||
110 | uint8_t *datbuf; | ||
111 | uint8_t *oobbuf; | ||
112 | }; | ||
113 | |||
70 | struct mtd_info { | 114 | struct mtd_info { |
71 | u_char type; | 115 | u_char type; |
72 | u_int32_t flags; | 116 | u_int32_t flags; |
@@ -125,8 +169,10 @@ struct mtd_info { | |||
125 | int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); | 169 | int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); |
126 | int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); | 170 | int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); |
127 | 171 | ||
128 | int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); | 172 | int (*read_oob) (struct mtd_info *mtd, loff_t from, |
129 | int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); | 173 | struct mtd_oob_ops *ops); |
174 | int (*write_oob) (struct mtd_info *mtd, loff_t to, | ||
175 | struct mtd_oob_ops *ops); | ||
130 | 176 | ||
131 | /* | 177 | /* |
132 | * Methods to access the protection register area, present in some | 178 | * Methods to access the protection register area, present in some |
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index dc2bf1bcf42b..bf2ce68901f5 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
@@ -31,14 +31,6 @@ extern int nand_scan (struct mtd_info *mtd, int max_chips); | |||
31 | /* Free resources held by the NAND device */ | 31 | /* Free resources held by the NAND device */ |
32 | extern void nand_release (struct mtd_info *mtd); | 32 | extern void nand_release (struct mtd_info *mtd); |
33 | 33 | ||
34 | /* Read raw data from the device without ECC */ | ||
35 | extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, | ||
36 | size_t len, size_t ooblen); | ||
37 | |||
38 | |||
39 | extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, | ||
40 | size_t *retlen, const uint8_t *buf, uint8_t *oob); | ||
41 | |||
42 | /* The maximum number of NAND chips in an array */ | 34 | /* The maximum number of NAND chips in an array */ |
43 | #define NAND_MAX_CHIPS 8 | 35 | #define NAND_MAX_CHIPS 8 |
44 | 36 | ||
@@ -375,6 +367,8 @@ struct nand_chip { | |||
375 | struct nand_buffers buffers; | 367 | struct nand_buffers buffers; |
376 | struct nand_hw_control hwcontrol; | 368 | struct nand_hw_control hwcontrol; |
377 | 369 | ||
370 | struct mtd_oob_ops ops; | ||
371 | |||
378 | uint8_t *bbt; | 372 | uint8_t *bbt; |
379 | struct nand_bbt_descr *bbt_td; | 373 | struct nand_bbt_descr *bbt_td; |
380 | struct nand_bbt_descr *bbt_md; | 374 | struct nand_bbt_descr *bbt_md; |