diff options
Diffstat (limited to 'drivers/mtd/onenand/onenand_base.c')
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 157 |
1 files changed, 136 insertions, 21 deletions
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 3d6f880cba9c..f690c1916d1d 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -304,16 +304,16 @@ static int onenand_wait(struct mtd_info *mtd, int state) | |||
304 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | 304 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); |
305 | 305 | ||
306 | if (ctrl & ONENAND_CTRL_ERROR) { | 306 | if (ctrl & ONENAND_CTRL_ERROR) { |
307 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); | 307 | printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); |
308 | if (ctrl & ONENAND_CTRL_LOCK) | 308 | if (ctrl & ONENAND_CTRL_LOCK) |
309 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n"); | 309 | printk(KERN_ERR "onenand_wait: it's locked error.\n"); |
310 | return ctrl; | 310 | return ctrl; |
311 | } | 311 | } |
312 | 312 | ||
313 | if (interrupt & ONENAND_INT_READ) { | 313 | if (interrupt & ONENAND_INT_READ) { |
314 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 314 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); |
315 | if (ecc) { | 315 | if (ecc) { |
316 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); | 316 | printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); |
317 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 317 | if (ecc & ONENAND_ECC_2BIT_ALL) { |
318 | mtd->ecc_stats.failed++; | 318 | mtd->ecc_stats.failed++; |
319 | return ecc; | 319 | return ecc; |
@@ -703,7 +703,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
703 | 703 | ||
704 | /* Do not allow reads past end of device */ | 704 | /* Do not allow reads past end of device */ |
705 | if ((from + len) > mtd->size) { | 705 | if ((from + len) > mtd->size) { |
706 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n"); | 706 | printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n"); |
707 | *retlen = 0; | 707 | *retlen = 0; |
708 | return -EINVAL; | 708 | return -EINVAL; |
709 | } | 709 | } |
@@ -834,7 +834,7 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col | |||
834 | * | 834 | * |
835 | * OneNAND read out-of-band data from the spare area | 835 | * OneNAND read out-of-band data from the spare area |
836 | */ | 836 | */ |
837 | int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 837 | static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, |
838 | size_t *retlen, u_char *buf, mtd_oob_mode_t mode) | 838 | size_t *retlen, u_char *buf, mtd_oob_mode_t mode) |
839 | { | 839 | { |
840 | struct onenand_chip *this = mtd->priv; | 840 | struct onenand_chip *this = mtd->priv; |
@@ -854,7 +854,7 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
854 | column = from & (mtd->oobsize - 1); | 854 | column = from & (mtd->oobsize - 1); |
855 | 855 | ||
856 | if (unlikely(column >= oobsize)) { | 856 | if (unlikely(column >= oobsize)) { |
857 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to start read outside oob\n"); | 857 | printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n"); |
858 | return -EINVAL; | 858 | return -EINVAL; |
859 | } | 859 | } |
860 | 860 | ||
@@ -862,7 +862,7 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
862 | if (unlikely(from >= mtd->size || | 862 | if (unlikely(from >= mtd->size || |
863 | column + len > ((mtd->size >> this->page_shift) - | 863 | column + len > ((mtd->size >> this->page_shift) - |
864 | (from >> this->page_shift)) * oobsize)) { | 864 | (from >> this->page_shift)) * oobsize)) { |
865 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempted to read beyond end of device\n"); | 865 | printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n"); |
866 | return -EINVAL; | 866 | return -EINVAL; |
867 | } | 867 | } |
868 | 868 | ||
@@ -888,7 +888,7 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
888 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | 888 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); |
889 | 889 | ||
890 | if (ret) { | 890 | if (ret) { |
891 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); | 891 | printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret); |
892 | break; | 892 | break; |
893 | } | 893 | } |
894 | 894 | ||
@@ -936,6 +936,121 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | |||
936 | &ops->oobretlen, ops->oobbuf, ops->mode); | 936 | &ops->oobretlen, ops->oobbuf, ops->mode); |
937 | } | 937 | } |
938 | 938 | ||
939 | /** | ||
940 | * onenand_bbt_wait - [DEFAULT] wait until the command is done | ||
941 | * @param mtd MTD device structure | ||
942 | * @param state state to select the max. timeout value | ||
943 | * | ||
944 | * Wait for command done. | ||
945 | */ | ||
946 | static int onenand_bbt_wait(struct mtd_info *mtd, int state) | ||
947 | { | ||
948 | struct onenand_chip *this = mtd->priv; | ||
949 | unsigned long timeout; | ||
950 | unsigned int interrupt; | ||
951 | unsigned int ctrl; | ||
952 | |||
953 | /* The 20 msec is enough */ | ||
954 | timeout = jiffies + msecs_to_jiffies(20); | ||
955 | while (time_before(jiffies, timeout)) { | ||
956 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
957 | if (interrupt & ONENAND_INT_MASTER) | ||
958 | break; | ||
959 | } | ||
960 | /* To get correct interrupt status in timeout case */ | ||
961 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
962 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | ||
963 | |||
964 | if (ctrl & ONENAND_CTRL_ERROR) { | ||
965 | printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); | ||
966 | /* Initial bad block case */ | ||
967 | if (ctrl & ONENAND_CTRL_LOAD) | ||
968 | return ONENAND_BBT_READ_ERROR; | ||
969 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
970 | } | ||
971 | |||
972 | if (interrupt & ONENAND_INT_READ) { | ||
973 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | ||
974 | if (ecc & ONENAND_ECC_2BIT_ALL) | ||
975 | return ONENAND_BBT_READ_ERROR; | ||
976 | } else { | ||
977 | printk(KERN_ERR "onenand_bbt_wait: read timeout!" | ||
978 | "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); | ||
979 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
980 | } | ||
981 | |||
982 | return 0; | ||
983 | } | ||
984 | |||
985 | /** | ||
986 | * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan | ||
987 | * @param mtd MTD device structure | ||
988 | * @param from offset to read from | ||
989 | * @param @ops oob operation description structure | ||
990 | * | ||
991 | * OneNAND read out-of-band data from the spare area for bbt scan | ||
992 | */ | ||
993 | int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | ||
994 | struct mtd_oob_ops *ops) | ||
995 | { | ||
996 | struct onenand_chip *this = mtd->priv; | ||
997 | int read = 0, thislen, column; | ||
998 | int ret = 0; | ||
999 | size_t len = ops->ooblen; | ||
1000 | u_char *buf = ops->oobbuf; | ||
1001 | |||
1002 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, len); | ||
1003 | |||
1004 | /* Initialize return value */ | ||
1005 | ops->oobretlen = 0; | ||
1006 | |||
1007 | /* Do not allow reads past end of device */ | ||
1008 | if (unlikely((from + len) > mtd->size)) { | ||
1009 | printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); | ||
1010 | return ONENAND_BBT_READ_FATAL_ERROR; | ||
1011 | } | ||
1012 | |||
1013 | /* Grab the lock and see if the device is available */ | ||
1014 | onenand_get_device(mtd, FL_READING); | ||
1015 | |||
1016 | column = from & (mtd->oobsize - 1); | ||
1017 | |||
1018 | while (read < len) { | ||
1019 | cond_resched(); | ||
1020 | |||
1021 | thislen = mtd->oobsize - column; | ||
1022 | thislen = min_t(int, thislen, len); | ||
1023 | |||
1024 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | ||
1025 | |||
1026 | onenand_update_bufferram(mtd, from, 0); | ||
1027 | |||
1028 | ret = onenand_bbt_wait(mtd, FL_READING); | ||
1029 | if (ret) | ||
1030 | break; | ||
1031 | |||
1032 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
1033 | read += thislen; | ||
1034 | if (read == len) | ||
1035 | break; | ||
1036 | |||
1037 | buf += thislen; | ||
1038 | |||
1039 | /* Read more? */ | ||
1040 | if (read < len) { | ||
1041 | /* Update Page size */ | ||
1042 | from += mtd->writesize; | ||
1043 | column = 0; | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | /* Deselect and wake up anyone waiting on the device */ | ||
1048 | onenand_release_device(mtd); | ||
1049 | |||
1050 | ops->oobretlen = read; | ||
1051 | return ret; | ||
1052 | } | ||
1053 | |||
939 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE | 1054 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE |
940 | /** | 1055 | /** |
941 | * onenand_verify_oob - [GENERIC] verify the oob contents after a write | 1056 | * onenand_verify_oob - [GENERIC] verify the oob contents after a write |
@@ -1040,13 +1155,13 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1040 | 1155 | ||
1041 | /* Do not allow writes past end of device */ | 1156 | /* Do not allow writes past end of device */ |
1042 | if (unlikely((to + len) > mtd->size)) { | 1157 | if (unlikely((to + len) > mtd->size)) { |
1043 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n"); | 1158 | printk(KERN_ERR "onenand_write: Attempt write to past end of device\n"); |
1044 | return -EINVAL; | 1159 | return -EINVAL; |
1045 | } | 1160 | } |
1046 | 1161 | ||
1047 | /* Reject writes, which are not page aligned */ | 1162 | /* Reject writes, which are not page aligned */ |
1048 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { | 1163 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { |
1049 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n"); | 1164 | printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n"); |
1050 | return -EINVAL; | 1165 | return -EINVAL; |
1051 | } | 1166 | } |
1052 | 1167 | ||
@@ -1083,14 +1198,14 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1083 | onenand_update_bufferram(mtd, to, !ret && !subpage); | 1198 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
1084 | 1199 | ||
1085 | if (ret) { | 1200 | if (ret) { |
1086 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); | 1201 | printk(KERN_ERR "onenand_write: write filaed %d\n", ret); |
1087 | break; | 1202 | break; |
1088 | } | 1203 | } |
1089 | 1204 | ||
1090 | /* Only check verify write turn on */ | 1205 | /* Only check verify write turn on */ |
1091 | ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); | 1206 | ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); |
1092 | if (ret) { | 1207 | if (ret) { |
1093 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); | 1208 | printk(KERN_ERR "onenand_write: verify failed %d\n", ret); |
1094 | break; | 1209 | break; |
1095 | } | 1210 | } |
1096 | 1211 | ||
@@ -1180,13 +1295,13 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1180 | column = to & (mtd->oobsize - 1); | 1295 | column = to & (mtd->oobsize - 1); |
1181 | 1296 | ||
1182 | if (unlikely(column >= oobsize)) { | 1297 | if (unlikely(column >= oobsize)) { |
1183 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to start write outside oob\n"); | 1298 | printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n"); |
1184 | return -EINVAL; | 1299 | return -EINVAL; |
1185 | } | 1300 | } |
1186 | 1301 | ||
1187 | /* For compatibility with NAND: Do not allow write past end of page */ | 1302 | /* For compatibility with NAND: Do not allow write past end of page */ |
1188 | if (column + len > oobsize) { | 1303 | if (column + len > oobsize) { |
1189 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: " | 1304 | printk(KERN_ERR "onenand_write_oob: " |
1190 | "Attempt to write past end of page\n"); | 1305 | "Attempt to write past end of page\n"); |
1191 | return -EINVAL; | 1306 | return -EINVAL; |
1192 | } | 1307 | } |
@@ -1195,7 +1310,7 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1195 | if (unlikely(to >= mtd->size || | 1310 | if (unlikely(to >= mtd->size || |
1196 | column + len > ((mtd->size >> this->page_shift) - | 1311 | column + len > ((mtd->size >> this->page_shift) - |
1197 | (to >> this->page_shift)) * oobsize)) { | 1312 | (to >> this->page_shift)) * oobsize)) { |
1198 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempted to write past end of device\n"); | 1313 | printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n"); |
1199 | return -EINVAL; | 1314 | return -EINVAL; |
1200 | } | 1315 | } |
1201 | 1316 | ||
@@ -1225,13 +1340,13 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1225 | 1340 | ||
1226 | ret = this->wait(mtd, FL_WRITING); | 1341 | ret = this->wait(mtd, FL_WRITING); |
1227 | if (ret) { | 1342 | if (ret) { |
1228 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write failed %d\n", ret); | 1343 | printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret); |
1229 | break; | 1344 | break; |
1230 | } | 1345 | } |
1231 | 1346 | ||
1232 | ret = onenand_verify_oob(mtd, this->page_buf, to); | 1347 | ret = onenand_verify_oob(mtd, this->page_buf, to); |
1233 | if (ret) { | 1348 | if (ret) { |
1234 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); | 1349 | printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); |
1235 | break; | 1350 | break; |
1236 | } | 1351 | } |
1237 | 1352 | ||
@@ -1314,19 +1429,19 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1314 | 1429 | ||
1315 | /* Start address must align on block boundary */ | 1430 | /* Start address must align on block boundary */ |
1316 | if (unlikely(instr->addr & (block_size - 1))) { | 1431 | if (unlikely(instr->addr & (block_size - 1))) { |
1317 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); | 1432 | printk(KERN_ERR "onenand_erase: Unaligned address\n"); |
1318 | return -EINVAL; | 1433 | return -EINVAL; |
1319 | } | 1434 | } |
1320 | 1435 | ||
1321 | /* Length must align on block boundary */ | 1436 | /* Length must align on block boundary */ |
1322 | if (unlikely(instr->len & (block_size - 1))) { | 1437 | if (unlikely(instr->len & (block_size - 1))) { |
1323 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); | 1438 | printk(KERN_ERR "onenand_erase: Length not block aligned\n"); |
1324 | return -EINVAL; | 1439 | return -EINVAL; |
1325 | } | 1440 | } |
1326 | 1441 | ||
1327 | /* Do not allow erase past end of device */ | 1442 | /* Do not allow erase past end of device */ |
1328 | if (unlikely((instr->len + instr->addr) > mtd->size)) { | 1443 | if (unlikely((instr->len + instr->addr) > mtd->size)) { |
1329 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); | 1444 | printk(KERN_ERR "onenand_erase: Erase past end of device\n"); |
1330 | return -EINVAL; | 1445 | return -EINVAL; |
1331 | } | 1446 | } |
1332 | 1447 | ||
@@ -1356,7 +1471,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1356 | ret = this->wait(mtd, FL_ERASING); | 1471 | ret = this->wait(mtd, FL_ERASING); |
1357 | /* Check, if it is write protected */ | 1472 | /* Check, if it is write protected */ |
1358 | if (ret) { | 1473 | if (ret) { |
1359 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); | 1474 | printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); |
1360 | instr->state = MTD_ERASE_FAILED; | 1475 | instr->state = MTD_ERASE_FAILED; |
1361 | instr->fail_addr = addr; | 1476 | instr->fail_addr = addr; |
1362 | goto erase_exit; | 1477 | goto erase_exit; |