aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKyungmin Park <kyungmin.park@samsung.com>2005-09-03 02:15:48 -0400
committerThomas Gleixner <tglx@mtd.linutronix.de>2005-11-06 15:20:53 -0500
commitcdc001305da4f057353911018e28f26f8f879061 (patch)
tree3672f12af5da82b646aee7ed2d923225fe279a3b
parent52b0eea73de05df33c51ca652e288a3ba1bba03b (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/Makefile4
-rw-r--r--drivers/mtd/onenand/onenand_base.c105
-rw-r--r--include/linux/mtd/onenand.h9
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.
6obj-$(CONFIG_MTD_ONENAND) += onenand_base.o 6obj-$(CONFIG_MTD_ONENAND) += onenand.o
7 7
8# Board specific. 8# Board specific.
9obj-$(CONFIG_MTD_ONENAND_OMAP) += omap-onenand.o 9obj-$(CONFIG_MTD_ONENAND_OMAP) += omap-onenand.o
10
11onenand-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 */
1074static 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 */
1169static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) 1198static 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 */
1215static 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 */
1188static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) 1240static 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 */
76struct onenand_chip { 81struct 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