aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/onenand/onenand_base.c
diff options
context:
space:
mode:
authorAdrian Hunter <ext-adrian.hunter@nokia.com>2007-01-31 10:19:28 -0500
committerKyungmin Park <kyungmin.park@samsung.com>2007-01-31 19:28:18 -0500
commita5e7c7b447270d42c3eb4d2259f74019aca9d007 (patch)
treeb454389e425b43c36d23a579e5e2da55ab3f2b00 /drivers/mtd/onenand/onenand_base.c
parent9bfbc9b24f663b15149874a94a69ba89b3b7e44c (diff)
[MTD] OneNAND: Add support for auto-placement of out-of-band data
Enable the use of oob operation mode MTD_OOB_AUTO with OneNAND. Note that MTD_OOB_RAW is still not supported. Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Diffstat (limited to 'drivers/mtd/onenand/onenand_base.c')
-rw-r--r--drivers/mtd/onenand/onenand_base.c204
1 files changed, 169 insertions, 35 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index daf298948b9b..67efbc70019e 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -787,20 +787,60 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
787} 787}
788 788
789/** 789/**
790 * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
791 * @param mtd MTD device structure
792 * @param buf destination address
793 * @param column oob offset to read from
794 * @param thislen oob length to read
795 */
796static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column,
797 int thislen)
798{
799 struct onenand_chip *this = mtd->priv;
800 struct nand_oobfree *free;
801 int readcol = column;
802 int readend = column + thislen;
803 int lastgap = 0;
804 uint8_t *oob_buf = this->page_buf + mtd->writesize;
805
806 for (free = this->ecclayout->oobfree; free->length; ++free) {
807 if (readcol >= lastgap)
808 readcol += free->offset - lastgap;
809 if (readend >= lastgap)
810 readend += free->offset - lastgap;
811 lastgap = free->offset + free->length;
812 }
813 this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf + readcol,
814 readcol, readend - readcol);
815 for (free = this->ecclayout->oobfree; free->length; ++free) {
816 int free_end = free->offset + free->length;
817 if (free->offset < readend && free_end > readcol) {
818 int st = max_t(int,free->offset,readcol);
819 int ed = min_t(int,free_end,readend);
820 int n = ed - st;
821 memcpy(buf, oob_buf + st, n);
822 buf += n;
823 }
824 }
825 return 0;
826}
827
828/**
790 * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band 829 * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band
791 * @param mtd MTD device structure 830 * @param mtd MTD device structure
792 * @param from offset to read from 831 * @param from offset to read from
793 * @param len number of bytes to read 832 * @param len number of bytes to read
794 * @param retlen pointer to variable to store the number of read bytes 833 * @param retlen pointer to variable to store the number of read bytes
795 * @param buf the databuffer to put data 834 * @param buf the databuffer to put data
835 * @param mode operation mode
796 * 836 *
797 * OneNAND read out-of-band data from the spare area 837 * OneNAND read out-of-band data from the spare area
798 */ 838 */
799int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, 839int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
800 size_t *retlen, u_char *buf) 840 size_t *retlen, u_char *buf, mtd_oob_mode_t mode)
801{ 841{
802 struct onenand_chip *this = mtd->priv; 842 struct onenand_chip *this = mtd->priv;
803 int read = 0, thislen, column; 843 int read = 0, thislen, column, oobsize;
804 int ret = 0; 844 int ret = 0;
805 845
806 DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); 846 DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
@@ -808,21 +848,33 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
808 /* Initialize return length value */ 848 /* Initialize return length value */
809 *retlen = 0; 849 *retlen = 0;
810 850
851 if (mode == MTD_OOB_AUTO)
852 oobsize = this->ecclayout->oobavail;
853 else
854 oobsize = mtd->oobsize;
855
856 column = from & (mtd->oobsize - 1);
857
858 if (unlikely(column >= oobsize)) {
859 DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to start read outside oob\n");
860 return -EINVAL;
861 }
862
811 /* Do not allow reads past end of device */ 863 /* Do not allow reads past end of device */
812 if (unlikely((from + len) > mtd->size)) { 864 if (unlikely(from >= mtd->size ||
813 DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); 865 column + len > ((mtd->size >> this->page_shift) -
866 (from >> this->page_shift)) * oobsize)) {
867 DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to read beyond end of device\n");
814 return -EINVAL; 868 return -EINVAL;
815 } 869 }
816 870
817 /* Grab the lock and see if the device is available */ 871 /* Grab the lock and see if the device is available */
818 onenand_get_device(mtd, FL_READING); 872 onenand_get_device(mtd, FL_READING);
819 873
820 column = from & (mtd->oobsize - 1);
821
822 while (read < len) { 874 while (read < len) {
823 cond_resched(); 875 cond_resched();
824 876
825 thislen = mtd->oobsize - column; 877 thislen = oobsize - column;
826 thislen = min_t(int, thislen, len); 878 thislen = min_t(int, thislen, len);
827 879
828 this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); 880 this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
@@ -832,7 +884,10 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
832 ret = this->wait(mtd, FL_READING); 884 ret = this->wait(mtd, FL_READING);
833 /* First copy data and check return value for ECC handling */ 885 /* First copy data and check return value for ECC handling */
834 886
835 this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); 887 if (mode == MTD_OOB_AUTO)
888 onenand_transfer_auto_oob(mtd, buf, column, thislen);
889 else
890 this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);
836 891
837 if (ret) { 892 if (ret) {
838 DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); 893 DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret);
@@ -871,10 +926,18 @@ out:
871static int onenand_read_oob(struct mtd_info *mtd, loff_t from, 926static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
872 struct mtd_oob_ops *ops) 927 struct mtd_oob_ops *ops)
873{ 928{
874 BUG_ON(ops->mode != MTD_OOB_PLACE); 929 switch (ops->mode)
875 930 {
931 case MTD_OOB_PLACE:
932 case MTD_OOB_AUTO:
933 break;
934 case MTD_OOB_RAW:
935 return -EINVAL; /* Not implemented yet */
936 default:
937 return -EINVAL;
938 }
876 return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, 939 return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen,
877 &ops->oobretlen, ops->oobbuf); 940 &ops->oobretlen, ops->oobbuf, ops->mode);
878} 941}
879 942
880#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE 943#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
@@ -883,14 +946,12 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
883 * @param mtd MTD device structure 946 * @param mtd MTD device structure
884 * @param buf the databuffer to verify 947 * @param buf the databuffer to verify
885 * @param to offset to read from 948 * @param to offset to read from
886 * @param len number of bytes to read and compare
887 * 949 *
888 */ 950 */
889static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len) 951static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
890{ 952{
891 struct onenand_chip *this = mtd->priv; 953 struct onenand_chip *this = mtd->priv;
892 char *readp = this->page_buf; 954 char *readp = this->page_buf + mtd->writesize;
893 int column = to & (mtd->oobsize - 1);
894 int status, i; 955 int status, i;
895 956
896 this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); 957 this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
@@ -899,9 +960,8 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
899 if (status) 960 if (status)
900 return status; 961 return status;
901 962
902 this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len); 963 this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize);
903 964 for(i = 0; i < mtd->oobsize; i++)
904 for(i = 0; i < len; i++)
905 if (buf[i] != 0xFF && buf[i] != readp[i]) 965 if (buf[i] != 0xFF && buf[i] != readp[i])
906 return -EBADMSG; 966 return -EBADMSG;
907 967
@@ -1060,20 +1120,59 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
1060} 1120}
1061 1121
1062/** 1122/**
1123 * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
1124 * @param mtd MTD device structure
1125 * @param oob_buf oob buffer
1126 * @param buf source address
1127 * @param column oob offset to write to
1128 * @param thislen oob length to write
1129 */
1130static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
1131 const u_char *buf, int column, int thislen)
1132{
1133 struct onenand_chip *this = mtd->priv;
1134 struct nand_oobfree *free;
1135 int writecol = column;
1136 int writeend = column + thislen;
1137 int lastgap = 0;
1138
1139 for (free = this->ecclayout->oobfree; free->length; ++free) {
1140 if (writecol >= lastgap)
1141 writecol += free->offset - lastgap;
1142 if (writeend >= lastgap)
1143 writeend += free->offset - lastgap;
1144 lastgap = free->offset + free->length;
1145 }
1146 writeend = mtd->oobsize;
1147 for (free = this->ecclayout->oobfree; free->length; ++free) {
1148 int free_end = free->offset + free->length;
1149 if (free->offset < writeend && free_end > writecol) {
1150 int st = max_t(int,free->offset,writecol);
1151 int ed = min_t(int,free_end,writeend);
1152 int n = ed - st;
1153 memcpy(oob_buf + st, buf, n);
1154 buf += n;
1155 }
1156 }
1157 return 0;
1158}
1159
1160/**
1063 * onenand_do_write_oob - [Internal] OneNAND write out-of-band 1161 * onenand_do_write_oob - [Internal] OneNAND write out-of-band
1064 * @param mtd MTD device structure 1162 * @param mtd MTD device structure
1065 * @param to offset to write to 1163 * @param to offset to write to
1066 * @param len number of bytes to write 1164 * @param len number of bytes to write
1067 * @param retlen pointer to variable to store the number of written bytes 1165 * @param retlen pointer to variable to store the number of written bytes
1068 * @param buf the data to write 1166 * @param buf the data to write
1167 * @param mode operation mode
1069 * 1168 *
1070 * OneNAND write out-of-band 1169 * OneNAND write out-of-band
1071 */ 1170 */
1072static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, 1171static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
1073 size_t *retlen, const u_char *buf) 1172 size_t *retlen, const u_char *buf, mtd_oob_mode_t mode)
1074{ 1173{
1075 struct onenand_chip *this = mtd->priv; 1174 struct onenand_chip *this = mtd->priv;
1076 int column, ret = 0; 1175 int column, ret = 0, oobsize;
1077 int written = 0; 1176 int written = 0;
1078 1177
1079 DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); 1178 DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@@ -1081,9 +1180,23 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
1081 /* Initialize retlen, in case of early exit */ 1180 /* Initialize retlen, in case of early exit */
1082 *retlen = 0; 1181 *retlen = 0;
1083 1182
1084 /* Do not allow writes past end of device */ 1183 if (mode == MTD_OOB_AUTO)
1085 if (unlikely((to + len) > mtd->size)) { 1184 oobsize = this->ecclayout->oobavail;
1086 DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); 1185 else
1186 oobsize = mtd->oobsize;
1187
1188 column = to & (mtd->oobsize - 1);
1189
1190 if (unlikely(column >= oobsize)) {
1191 DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to start write outside oob\n");
1192 return -EINVAL;
1193 }
1194
1195 /* Do not allow reads past end of device */
1196 if (unlikely(to >= mtd->size ||
1197 column + len > ((mtd->size >> this->page_shift) -
1198 (to >> this->page_shift)) * oobsize)) {
1199 DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to write past end of device\n");
1087 return -EINVAL; 1200 return -EINVAL;
1088 } 1201 }
1089 1202
@@ -1092,18 +1205,19 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
1092 1205
1093 /* Loop until all data write */ 1206 /* Loop until all data write */
1094 while (written < len) { 1207 while (written < len) {
1095 int thislen = min_t(int, mtd->oobsize, len - written); 1208 int thislen = min_t(int, oobsize, len - written);
1096 1209
1097 cond_resched(); 1210 cond_resched();
1098 1211
1099 column = to & (mtd->oobsize - 1);
1100
1101 this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); 1212 this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
1102 1213
1103 /* We send data to spare ram with oobsize 1214 /* We send data to spare ram with oobsize
1104 * to prevent byte access */ 1215 * to prevent byte access */
1105 memset(this->page_buf, 0xff, mtd->oobsize); 1216 memset(this->page_buf, 0xff, mtd->oobsize);
1106 memcpy(this->page_buf + column, buf, thislen); 1217 if (mode == MTD_OOB_AUTO)
1218 onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen);
1219 else
1220 memcpy(this->page_buf + column, buf, thislen);
1107 this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); 1221 this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize);
1108 1222
1109 this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); 1223 this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
@@ -1112,11 +1226,11 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
1112 1226
1113 ret = this->wait(mtd, FL_WRITING); 1227 ret = this->wait(mtd, FL_WRITING);
1114 if (ret) { 1228 if (ret) {
1115 DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret); 1229 DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret);
1116 goto out; 1230 goto out;
1117 } 1231 }
1118 1232
1119 ret = onenand_verify_oob(mtd, buf, to, thislen); 1233 ret = onenand_verify_oob(mtd, this->page_buf, to);
1120 if (ret) { 1234 if (ret) {
1121 DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); 1235 DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret);
1122 goto out; 1236 goto out;
@@ -1127,8 +1241,9 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
1127 if (written == len) 1241 if (written == len)
1128 break; 1242 break;
1129 1243
1130 to += thislen; 1244 to += mtd->writesize;
1131 buf += thislen; 1245 buf += thislen;
1246 column = 0;
1132 } 1247 }
1133 1248
1134out: 1249out:
@@ -1149,10 +1264,18 @@ out:
1149static int onenand_write_oob(struct mtd_info *mtd, loff_t to, 1264static int onenand_write_oob(struct mtd_info *mtd, loff_t to,
1150 struct mtd_oob_ops *ops) 1265 struct mtd_oob_ops *ops)
1151{ 1266{
1152 BUG_ON(ops->mode != MTD_OOB_PLACE); 1267 switch (ops->mode)
1153 1268 {
1269 case MTD_OOB_PLACE:
1270 case MTD_OOB_AUTO:
1271 break;
1272 case MTD_OOB_RAW:
1273 return -EINVAL; /* Not implemented yet */
1274 default:
1275 return -EINVAL;
1276 }
1154 return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, 1277 return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen,
1155 &ops->oobretlen, ops->oobbuf); 1278 &ops->oobretlen, ops->oobbuf, ops->mode);
1156} 1279}
1157 1280
1158/** 1281/**
@@ -1318,7 +1441,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
1318 1441
1319 /* We write two bytes, so we dont have to mess with 16 bit access */ 1442 /* We write two bytes, so we dont have to mess with 16 bit access */
1320 ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); 1443 ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
1321 return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); 1444 return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE);
1322} 1445}
1323 1446
1324/** 1447/**
@@ -1612,7 +1735,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
1612 this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); 1735 this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
1613 this->wait(mtd, FL_OTPING); 1736 this->wait(mtd, FL_OTPING);
1614 1737
1615 ret = onenand_do_write_oob(mtd, from, len, retlen, buf); 1738 ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE);
1616 1739
1617 /* Exit OTP access mode */ 1740 /* Exit OTP access mode */
1618 this->command(mtd, ONENAND_CMD_RESET, 0, 0); 1741 this->command(mtd, ONENAND_CMD_RESET, 0, 0);
@@ -2019,6 +2142,7 @@ static void onenand_resume(struct mtd_info *mtd)
2019 */ 2142 */
2020int onenand_scan(struct mtd_info *mtd, int maxchips) 2143int onenand_scan(struct mtd_info *mtd, int maxchips)
2021{ 2144{
2145 int i;
2022 struct onenand_chip *this = mtd->priv; 2146 struct onenand_chip *this = mtd->priv;
2023 2147
2024 if (!this->read_word) 2148 if (!this->read_word)
@@ -2090,6 +2214,16 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
2090 } 2214 }
2091 2215
2092 this->subpagesize = mtd->writesize >> mtd->subpage_sft; 2216 this->subpagesize = mtd->writesize >> mtd->subpage_sft;
2217
2218 /*
2219 * The number of bytes available for a client to place data into
2220 * the out of band area
2221 */
2222 this->ecclayout->oobavail = 0;
2223 for (i = 0; this->ecclayout->oobfree[i].length; i++)
2224 this->ecclayout->oobavail +=
2225 this->ecclayout->oobfree[i].length;
2226
2093 mtd->ecclayout = this->ecclayout; 2227 mtd->ecclayout = this->ecclayout;
2094 2228
2095 /* Fill in remaining MTD driver data */ 2229 /* Fill in remaining MTD driver data */