diff options
author | Kyungmin Park <kyungmin.park@samsung.com> | 2005-09-03 02:15:48 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@mtd.linutronix.de> | 2005-11-06 15:20:53 -0500 |
commit | cdc001305da4f057353911018e28f26f8f879061 (patch) | |
tree | 3672f12af5da82b646aee7ed2d923225fe279a3b | |
parent | 52b0eea73de05df33c51ca652e288a3ba1bba03b (diff) |
[PATCH] OneNAND: Simple Bad Block handling support
Based on NAND memory bad block table code
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | drivers/mtd/onenand/Makefile | 4 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 105 | ||||
-rw-r--r-- | include/linux/mtd/onenand.h | 9 |
3 files changed, 98 insertions, 20 deletions
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index f4e75864d8b5..243c7592a111 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile | |||
@@ -3,7 +3,9 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | # Core functionality. | 5 | # Core functionality. |
6 | obj-$(CONFIG_MTD_ONENAND) += onenand_base.o | 6 | obj-$(CONFIG_MTD_ONENAND) += onenand.o |
7 | 7 | ||
8 | # Board specific. | 8 | # Board specific. |
9 | obj-$(CONFIG_MTD_ONENAND_OMAP) += omap-onenand.o | 9 | obj-$(CONFIG_MTD_ONENAND_OMAP) += omap-onenand.o |
10 | |||
11 | onenand-objs = onenand_base.o onenand_bbt.o | ||
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index e87489505772..bdeac01e659f 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -311,19 +311,21 @@ static int onenand_wait(struct mtd_info *mtd, int state) | |||
311 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | 311 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); |
312 | 312 | ||
313 | if (ctrl & ONENAND_CTRL_ERROR) { | 313 | if (ctrl & ONENAND_CTRL_ERROR) { |
314 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x", ctrl); | 314 | /* It maybe occur at initial bad block */ |
315 | return -EIO; | 315 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); |
316 | /* Clear other interrupt bits for preventing ECC error */ | ||
317 | interrupt &= ONENAND_INT_MASTER; | ||
316 | } | 318 | } |
317 | 319 | ||
318 | if (ctrl & ONENAND_CTRL_LOCK) { | 320 | if (ctrl & ONENAND_CTRL_LOCK) { |
319 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x", ctrl); | 321 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x\n", ctrl); |
320 | return -EIO; | 322 | return -EACCES; |
321 | } | 323 | } |
322 | 324 | ||
323 | if (interrupt & ONENAND_INT_READ) { | 325 | if (interrupt & ONENAND_INT_READ) { |
324 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 326 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); |
325 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 327 | if (ecc & ONENAND_ECC_2BIT_ALL) { |
326 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x", ecc); | 328 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); |
327 | return -EBADMSG; | 329 | return -EBADMSG; |
328 | } | 330 | } |
329 | } | 331 | } |
@@ -1060,6 +1062,25 @@ static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs, | |||
1060 | } | 1062 | } |
1061 | 1063 | ||
1062 | /** | 1064 | /** |
1065 | * onenand_block_checkbad - [GENERIC] Check if a block is marked bad | ||
1066 | * @param mtd MTD device structure | ||
1067 | * @param ofs offset from device start | ||
1068 | * @param getchip 0, if the chip is already selected | ||
1069 | * @param allowbbt 1, if its allowed to access the bbt area | ||
1070 | * | ||
1071 | * Check, if the block is bad. Either by reading the bad block table or | ||
1072 | * calling of the scan function. | ||
1073 | */ | ||
1074 | static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) | ||
1075 | { | ||
1076 | struct onenand_chip *this = mtd->priv; | ||
1077 | struct bbm_info *bbm = this->bbm; | ||
1078 | |||
1079 | /* Return info from the table */ | ||
1080 | return bbm->isbad_bbt(mtd, ofs, allowbbt); | ||
1081 | } | ||
1082 | |||
1083 | /** | ||
1063 | * onenand_erase - [MTD Interface] erase block(s) | 1084 | * onenand_erase - [MTD Interface] erase block(s) |
1064 | * @param mtd MTD device structure | 1085 | * @param mtd MTD device structure |
1065 | * @param instr erase instruction | 1086 | * @param instr erase instruction |
@@ -1109,7 +1130,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1109 | 1130 | ||
1110 | while (len) { | 1131 | while (len) { |
1111 | 1132 | ||
1112 | /* TODO Check badblock */ | 1133 | /* Check if we have a bad block, we do not erase bad blocks */ |
1134 | if (onenand_block_checkbad(mtd, addr, 0, 0)) { | ||
1135 | printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr); | ||
1136 | instr->state = MTD_ERASE_FAILED; | ||
1137 | goto erase_exit; | ||
1138 | } | ||
1113 | 1139 | ||
1114 | this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); | 1140 | this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); |
1115 | 1141 | ||
@@ -1161,34 +1187,70 @@ static void onenand_sync(struct mtd_info *mtd) | |||
1161 | onenand_release_device(mtd); | 1187 | onenand_release_device(mtd); |
1162 | } | 1188 | } |
1163 | 1189 | ||
1190 | |||
1164 | /** | 1191 | /** |
1165 | * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad | 1192 | * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad |
1166 | * @param mtd MTD device structure | 1193 | * @param mtd MTD device structure |
1167 | * @param ofs offset relative to mtd start | 1194 | * @param ofs offset relative to mtd start |
1195 | * | ||
1196 | * Check whether the block is bad | ||
1168 | */ | 1197 | */ |
1169 | static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) | 1198 | static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) |
1170 | { | 1199 | { |
1171 | /* | 1200 | /* Check for invalid offset */ |
1172 | * TODO | 1201 | if (ofs > mtd->size) |
1173 | * 1. Bad block table (BBT) | 1202 | return -EINVAL; |
1174 | * -> using NAND BBT to support JFFS2 | 1203 | |
1175 | * 2. Bad block management (BBM) | 1204 | return onenand_block_checkbad(mtd, ofs, 1, 0); |
1176 | * -> bad block replace scheme | 1205 | } |
1177 | * | 1206 | |
1178 | * Currently we do nothing | 1207 | /** |
1179 | */ | 1208 | * onenand_default_block_markbad - [DEFAULT] mark a block bad |
1180 | return 0; | 1209 | * @param mtd MTD device structure |
1210 | * @param ofs offset from device start | ||
1211 | * | ||
1212 | * This is the default implementation, which can be overridden by | ||
1213 | * a hardware specific driver. | ||
1214 | */ | ||
1215 | static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | ||
1216 | { | ||
1217 | struct onenand_chip *this = mtd->priv; | ||
1218 | struct bbm_info *bbm = this->bbm; | ||
1219 | u_char buf[2] = {0, 0}; | ||
1220 | size_t retlen; | ||
1221 | int block; | ||
1222 | |||
1223 | /* Get block number */ | ||
1224 | block = ((int) ofs) >> bbm->bbt_erase_shift; | ||
1225 | if (bbm->bbt) | ||
1226 | bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); | ||
1227 | |||
1228 | /* We write two bytes, so we dont have to mess with 16 bit access */ | ||
1229 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | ||
1230 | return mtd->write_oob(mtd, ofs , 2, &retlen, buf); | ||
1181 | } | 1231 | } |
1182 | 1232 | ||
1183 | /** | 1233 | /** |
1184 | * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad | 1234 | * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad |
1185 | * @param mtd MTD device structure | 1235 | * @param mtd MTD device structure |
1186 | * @param ofs offset relative to mtd start | 1236 | * @param ofs offset relative to mtd start |
1237 | * | ||
1238 | * Mark the block as bad | ||
1187 | */ | 1239 | */ |
1188 | static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) | 1240 | static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) |
1189 | { | 1241 | { |
1190 | /* see above */ | 1242 | struct onenand_chip *this = mtd->priv; |
1191 | return 0; | 1243 | int ret; |
1244 | |||
1245 | ret = onenand_block_isbad(mtd, ofs); | ||
1246 | if (ret) { | ||
1247 | /* If it was bad already, return success and do nothing */ | ||
1248 | if (ret > 0) | ||
1249 | return 0; | ||
1250 | return ret; | ||
1251 | } | ||
1252 | |||
1253 | return this->block_markbad(mtd, ofs); | ||
1192 | } | 1254 | } |
1193 | 1255 | ||
1194 | /** | 1256 | /** |
@@ -1411,6 +1473,11 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
1411 | if (!this->write_bufferram) | 1473 | if (!this->write_bufferram) |
1412 | this->write_bufferram = onenand_write_bufferram; | 1474 | this->write_bufferram = onenand_write_bufferram; |
1413 | 1475 | ||
1476 | if (!this->block_markbad) | ||
1477 | this->block_markbad = onenand_default_block_markbad; | ||
1478 | if (!this->scan_bbt) | ||
1479 | this->scan_bbt = onenand_default_bbt; | ||
1480 | |||
1414 | if (onenand_probe(mtd)) | 1481 | if (onenand_probe(mtd)) |
1415 | return -ENXIO; | 1482 | return -ENXIO; |
1416 | 1483 | ||
@@ -1472,7 +1539,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) | |||
1472 | /* Unlock whole block */ | 1539 | /* Unlock whole block */ |
1473 | mtd->unlock(mtd, 0x0, this->chipsize); | 1540 | mtd->unlock(mtd, 0x0, this->chipsize); |
1474 | 1541 | ||
1475 | return 0; | 1542 | return this->scan_bbt(mtd); |
1476 | } | 1543 | } |
1477 | 1544 | ||
1478 | /** | 1545 | /** |
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index c557caa24a6c..89aaffbc9576 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h | |||
@@ -14,6 +14,7 @@ | |||
14 | 14 | ||
15 | #include <linux/spinlock.h> | 15 | #include <linux/spinlock.h> |
16 | #include <linux/mtd/onenand_regs.h> | 16 | #include <linux/mtd/onenand_regs.h> |
17 | #include <linux/mtd/bbm.h> | ||
17 | 18 | ||
18 | #define MAX_BUFFERRAM 2 | 19 | #define MAX_BUFFERRAM 2 |
19 | 20 | ||
@@ -67,10 +68,14 @@ struct onenand_bufferram { | |||
67 | * @param wait [REPLACEABLE] hardware specific function for wait on ready | 68 | * @param wait [REPLACEABLE] hardware specific function for wait on ready |
68 | * @param read_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area | 69 | * @param read_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area |
69 | * @param write_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area | 70 | * @param write_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area |
71 | * @param read_word [REPLACEABLE] hardware specific function for read register of OneNAND | ||
72 | * @param write_word [REPLACEABLE] hardware specific function for write register of OneNAND | ||
73 | * @param scan_bbt [REPLACEALBE] hardware specific function for scaning Bad block Table | ||
70 | * @param chip_lock [INTERN] spinlock used to protect access to this structure and the chip | 74 | * @param chip_lock [INTERN] spinlock used to protect access to this structure and the chip |
71 | * @param wq [INTERN] wait queue to sleep on if a OneNAND operation is in progress | 75 | * @param wq [INTERN] wait queue to sleep on if a OneNAND operation is in progress |
72 | * @param state [INTERN] the current state of the OneNAND device | 76 | * @param state [INTERN] the current state of the OneNAND device |
73 | * @param autooob [REPLACEABLE] the default (auto)placement scheme | 77 | * @param autooob [REPLACEABLE] the default (auto)placement scheme |
78 | * @param bbm [REPLACEABLE] pointer to Bad Block Management | ||
74 | * @param priv [OPTIONAL] pointer to private chip date | 79 | * @param priv [OPTIONAL] pointer to private chip date |
75 | */ | 80 | */ |
76 | struct onenand_chip { | 81 | struct onenand_chip { |
@@ -96,6 +101,8 @@ struct onenand_chip { | |||
96 | unsigned short (*read_word)(void __iomem *addr); | 101 | unsigned short (*read_word)(void __iomem *addr); |
97 | void (*write_word)(unsigned short value, void __iomem *addr); | 102 | void (*write_word)(unsigned short value, void __iomem *addr); |
98 | void (*mmcontrol)(struct mtd_info *mtd, int sync_read); | 103 | void (*mmcontrol)(struct mtd_info *mtd, int sync_read); |
104 | int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); | ||
105 | int (*scan_bbt)(struct mtd_info *mtd); | ||
99 | 106 | ||
100 | spinlock_t chip_lock; | 107 | spinlock_t chip_lock; |
101 | wait_queue_head_t wq; | 108 | wait_queue_head_t wq; |
@@ -103,6 +110,8 @@ struct onenand_chip { | |||
103 | 110 | ||
104 | struct nand_oobinfo *autooob; | 111 | struct nand_oobinfo *autooob; |
105 | 112 | ||
113 | void *bbm; | ||
114 | |||
106 | void *priv; | 115 | void *priv; |
107 | }; | 116 | }; |
108 | 117 | ||