| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* Linux driver for NAND Flash Translation Layer      */ | 
 | 2 | /* (c) 1999 Machine Vision Holdings, Inc.             */ | 
 | 3 | /* Author: David Woodhouse <dwmw2@infradead.org>      */ | 
 | 4 | /* $Id: nftlcore.c,v 1.97 2004/11/16 18:28:59 dwmw2 Exp $ */ | 
 | 5 |  | 
 | 6 | /* | 
 | 7 |   The contents of this file are distributed under the GNU General | 
 | 8 |   Public License version 2. The author places no additional | 
 | 9 |   restrictions of any kind on it. | 
 | 10 |  */ | 
 | 11 |  | 
 | 12 | #define PRERELEASE | 
 | 13 |  | 
 | 14 | #include <linux/config.h> | 
 | 15 | #include <linux/kernel.h> | 
 | 16 | #include <linux/module.h> | 
 | 17 | #include <asm/errno.h> | 
 | 18 | #include <asm/io.h> | 
 | 19 | #include <asm/uaccess.h> | 
 | 20 | #include <linux/miscdevice.h> | 
 | 21 | #include <linux/pci.h> | 
 | 22 | #include <linux/delay.h> | 
 | 23 | #include <linux/slab.h> | 
 | 24 | #include <linux/sched.h> | 
 | 25 | #include <linux/init.h> | 
 | 26 | #include <linux/hdreg.h> | 
 | 27 |  | 
 | 28 | #include <linux/kmod.h> | 
 | 29 | #include <linux/mtd/mtd.h> | 
 | 30 | #include <linux/mtd/nand.h> | 
 | 31 | #include <linux/mtd/nftl.h> | 
 | 32 | #include <linux/mtd/blktrans.h> | 
 | 33 |  | 
 | 34 | /* maximum number of loops while examining next block, to have a | 
 | 35 |    chance to detect consistency problems (they should never happen | 
 | 36 |    because of the checks done in the mounting */ | 
 | 37 |  | 
 | 38 | #define MAX_LOOPS 10000 | 
 | 39 |  | 
 | 40 |  | 
 | 41 | static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | 
 | 42 | { | 
 | 43 | 	struct NFTLrecord *nftl; | 
 | 44 | 	unsigned long temp; | 
 | 45 |  | 
 | 46 | 	if (mtd->type != MTD_NANDFLASH) | 
 | 47 | 		return; | 
 | 48 | 	/* OK, this is moderately ugly.  But probably safe.  Alternatives? */ | 
 | 49 | 	if (memcmp(mtd->name, "DiskOnChip", 10)) | 
 | 50 | 		return; | 
 | 51 |  | 
 | 52 | 	if (!mtd->block_isbad) { | 
 | 53 | 		printk(KERN_ERR | 
 | 54 | "NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n" | 
 | 55 | "Please use the new diskonchip driver under the NAND subsystem.\n"); | 
 | 56 | 		return; | 
 | 57 | 	} | 
 | 58 |  | 
 | 59 | 	DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); | 
 | 60 |  | 
 | 61 | 	nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); | 
 | 62 |  | 
 | 63 | 	if (!nftl) { | 
 | 64 | 		printk(KERN_WARNING "NFTL: out of memory for data structures\n"); | 
 | 65 | 		return; | 
 | 66 | 	} | 
 | 67 | 	memset(nftl, 0, sizeof(*nftl)); | 
 | 68 |  | 
 | 69 | 	nftl->mbd.mtd = mtd; | 
 | 70 | 	nftl->mbd.devnum = -1; | 
 | 71 | 	nftl->mbd.blksize = 512; | 
 | 72 | 	nftl->mbd.tr = tr; | 
 | 73 | 	memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo)); | 
 | 74 | 	nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY; | 
 | 75 |  | 
 | 76 |         if (NFTL_mount(nftl) < 0) { | 
 | 77 | 		printk(KERN_WARNING "NFTL: could not mount device\n"); | 
 | 78 | 		kfree(nftl); | 
 | 79 | 		return; | 
 | 80 |         } | 
 | 81 |  | 
 | 82 | 	/* OK, it's a new one. Set up all the data structures. */ | 
 | 83 |  | 
 | 84 | 	/* Calculate geometry */ | 
 | 85 | 	nftl->cylinders = 1024; | 
 | 86 | 	nftl->heads = 16; | 
 | 87 |  | 
 | 88 | 	temp = nftl->cylinders * nftl->heads; | 
 | 89 | 	nftl->sectors = nftl->mbd.size / temp; | 
 | 90 | 	if (nftl->mbd.size % temp) { | 
 | 91 | 		nftl->sectors++; | 
 | 92 | 		temp = nftl->cylinders * nftl->sectors; | 
 | 93 | 		nftl->heads = nftl->mbd.size / temp; | 
 | 94 |  | 
 | 95 | 		if (nftl->mbd.size % temp) { | 
 | 96 | 			nftl->heads++; | 
 | 97 | 			temp = nftl->heads * nftl->sectors; | 
 | 98 | 			nftl->cylinders = nftl->mbd.size / temp; | 
 | 99 | 		} | 
 | 100 | 	} | 
 | 101 |  | 
 | 102 | 	if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) { | 
 | 103 | 		/* | 
 | 104 | 		  Oh no we don't have  | 
 | 105 | 		   mbd.size == heads * cylinders * sectors | 
 | 106 | 		*/ | 
 | 107 | 		printk(KERN_WARNING "NFTL: cannot calculate a geometry to " | 
 | 108 | 		       "match size of 0x%lx.\n", nftl->mbd.size); | 
 | 109 | 		printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d " | 
 | 110 | 			"(== 0x%lx sects)\n", | 
 | 111 | 			nftl->cylinders, nftl->heads , nftl->sectors,  | 
 | 112 | 			(long)nftl->cylinders * (long)nftl->heads * | 
 | 113 | 			(long)nftl->sectors ); | 
 | 114 | 	} | 
 | 115 |  | 
 | 116 | 	if (add_mtd_blktrans_dev(&nftl->mbd)) { | 
 | 117 | 		if (nftl->ReplUnitTable) | 
 | 118 | 			kfree(nftl->ReplUnitTable); | 
 | 119 | 		if (nftl->EUNtable) | 
 | 120 | 			kfree(nftl->EUNtable); | 
 | 121 | 		kfree(nftl); | 
 | 122 | 		return; | 
 | 123 | 	} | 
 | 124 | #ifdef PSYCHO_DEBUG | 
 | 125 | 	printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); | 
 | 126 | #endif | 
 | 127 | } | 
 | 128 |  | 
 | 129 | static void nftl_remove_dev(struct mtd_blktrans_dev *dev) | 
 | 130 | { | 
 | 131 | 	struct NFTLrecord *nftl = (void *)dev; | 
 | 132 |  | 
 | 133 | 	DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum); | 
 | 134 |  | 
 | 135 | 	del_mtd_blktrans_dev(dev); | 
 | 136 | 	if (nftl->ReplUnitTable) | 
 | 137 | 		kfree(nftl->ReplUnitTable); | 
 | 138 | 	if (nftl->EUNtable) | 
 | 139 | 		kfree(nftl->EUNtable); | 
 | 140 | 	kfree(nftl); | 
 | 141 | } | 
 | 142 |  | 
 | 143 | #ifdef CONFIG_NFTL_RW | 
 | 144 |  | 
 | 145 | /* Actual NFTL access routines */ | 
 | 146 | /* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used | 
 | 147 |  *	when the give Virtual Unit Chain | 
 | 148 |  */ | 
 | 149 | static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate ) | 
 | 150 | { | 
 | 151 | 	/* For a given Virtual Unit Chain: find or create a free block and | 
 | 152 | 	   add it to the chain */ | 
 | 153 | 	/* We're passed the number of the last EUN in the chain, to save us from | 
 | 154 | 	   having to look it up again */ | 
 | 155 | 	u16 pot = nftl->LastFreeEUN; | 
 | 156 | 	int silly = nftl->nb_blocks; | 
 | 157 |  | 
 | 158 | 	/* Normally, we force a fold to happen before we run out of free blocks completely */ | 
 | 159 | 	if (!desperate && nftl->numfreeEUNs < 2) { | 
 | 160 | 		DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n"); | 
 | 161 | 		return 0xffff; | 
 | 162 | 	} | 
 | 163 |  | 
 | 164 | 	/* Scan for a free block */ | 
 | 165 | 	do { | 
 | 166 | 		if (nftl->ReplUnitTable[pot] == BLOCK_FREE) { | 
 | 167 | 			nftl->LastFreeEUN = pot; | 
 | 168 | 			nftl->numfreeEUNs--; | 
 | 169 | 			return pot; | 
 | 170 | 		} | 
 | 171 |  | 
 | 172 | 		/* This will probably point to the MediaHdr unit itself, | 
 | 173 | 		   right at the beginning of the partition. But that unit | 
 | 174 | 		   (and the backup unit too) should have the UCI set | 
 | 175 | 		   up so that it's not selected for overwriting */ | 
 | 176 | 		if (++pot > nftl->lastEUN) | 
 | 177 | 			pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN); | 
 | 178 |  | 
 | 179 | 		if (!silly--) { | 
 | 180 | 			printk("Argh! No free blocks found! LastFreeEUN = %d, " | 
 | 181 | 			       "FirstEUN = %d\n", nftl->LastFreeEUN,  | 
 | 182 | 			       le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN)); | 
 | 183 | 			return 0xffff; | 
 | 184 | 		} | 
 | 185 | 	} while (pot != nftl->LastFreeEUN); | 
 | 186 |  | 
 | 187 | 	return 0xffff; | 
 | 188 | } | 
 | 189 |  | 
 | 190 | static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock ) | 
 | 191 | { | 
 | 192 | 	u16 BlockMap[MAX_SECTORS_PER_UNIT]; | 
 | 193 | 	unsigned char BlockLastState[MAX_SECTORS_PER_UNIT]; | 
 | 194 | 	unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT]; | 
 | 195 | 	unsigned int thisEUN; | 
 | 196 | 	int block; | 
 | 197 | 	int silly; | 
 | 198 | 	unsigned int targetEUN; | 
 | 199 | 	struct nftl_oob oob; | 
 | 200 | 	int inplace = 1; | 
 | 201 |         size_t retlen; | 
 | 202 |  | 
 | 203 | 	memset(BlockMap, 0xff, sizeof(BlockMap)); | 
 | 204 | 	memset(BlockFreeFound, 0, sizeof(BlockFreeFound)); | 
 | 205 |  | 
 | 206 | 	thisEUN = nftl->EUNtable[thisVUC]; | 
 | 207 |  | 
 | 208 | 	if (thisEUN == BLOCK_NIL) { | 
 | 209 | 		printk(KERN_WARNING "Trying to fold non-existent " | 
 | 210 | 		       "Virtual Unit Chain %d!\n", thisVUC); | 
 | 211 | 		return BLOCK_NIL; | 
 | 212 | 	} | 
 | 213 | 	 | 
 | 214 | 	/* Scan to find the Erase Unit which holds the actual data for each | 
 | 215 | 	   512-byte block within the Chain. | 
 | 216 | 	*/ | 
 | 217 |         silly = MAX_LOOPS; | 
 | 218 | 	targetEUN = BLOCK_NIL; | 
 | 219 | 	while (thisEUN <= nftl->lastEUN ) { | 
 | 220 |                 unsigned int status, foldmark; | 
 | 221 |  | 
 | 222 | 		targetEUN = thisEUN; | 
 | 223 | 		for (block = 0; block < nftl->EraseSize / 512; block ++) { | 
 | 224 | 			MTD_READOOB(nftl->mbd.mtd, | 
 | 225 | 				    (thisEUN * nftl->EraseSize) + (block * 512), | 
 | 226 | 				    16 , &retlen, (char *)&oob); | 
 | 227 | 			if (block == 2) { | 
 | 228 |                                 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1; | 
 | 229 |                                 if (foldmark == FOLD_MARK_IN_PROGRESS) { | 
 | 230 |                                         DEBUG(MTD_DEBUG_LEVEL1,  | 
 | 231 |                                               "Write Inhibited on EUN %d\n", thisEUN); | 
 | 232 | 					inplace = 0; | 
 | 233 | 				} else { | 
 | 234 | 					/* There's no other reason not to do inplace, | 
 | 235 | 					   except ones that come later. So we don't need | 
 | 236 | 					   to preserve inplace */ | 
 | 237 | 					inplace = 1; | 
 | 238 | 				} | 
 | 239 | 			} | 
 | 240 |                         status = oob.b.Status | oob.b.Status1; | 
 | 241 | 			BlockLastState[block] = status; | 
 | 242 |  | 
 | 243 | 			switch(status) { | 
 | 244 | 			case SECTOR_FREE: | 
 | 245 | 				BlockFreeFound[block] = 1; | 
 | 246 | 				break; | 
 | 247 |  | 
 | 248 | 			case SECTOR_USED: | 
 | 249 | 				if (!BlockFreeFound[block]) | 
 | 250 | 					BlockMap[block] = thisEUN; | 
 | 251 | 				else | 
 | 252 | 					printk(KERN_WARNING  | 
 | 253 | 					       "SECTOR_USED found after SECTOR_FREE " | 
 | 254 | 					       "in Virtual Unit Chain %d for block %d\n", | 
 | 255 | 					       thisVUC, block); | 
 | 256 | 				break; | 
 | 257 | 			case SECTOR_DELETED: | 
 | 258 | 				if (!BlockFreeFound[block]) | 
 | 259 | 					BlockMap[block] = BLOCK_NIL; | 
 | 260 | 				else | 
 | 261 | 					printk(KERN_WARNING  | 
 | 262 | 					       "SECTOR_DELETED found after SECTOR_FREE " | 
 | 263 | 					       "in Virtual Unit Chain %d for block %d\n", | 
 | 264 | 					       thisVUC, block); | 
 | 265 | 				break; | 
 | 266 |  | 
 | 267 | 			case SECTOR_IGNORE: | 
 | 268 | 				break; | 
 | 269 | 			default: | 
 | 270 | 				printk("Unknown status for block %d in EUN %d: %x\n", | 
 | 271 | 				       block, thisEUN, status); | 
 | 272 | 			} | 
 | 273 | 		} | 
 | 274 |  | 
 | 275 | 		if (!silly--) { | 
 | 276 | 			printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", | 
 | 277 | 			       thisVUC); | 
 | 278 | 			return BLOCK_NIL; | 
 | 279 | 		} | 
 | 280 | 		 | 
 | 281 | 		thisEUN = nftl->ReplUnitTable[thisEUN]; | 
 | 282 | 	} | 
 | 283 |  | 
 | 284 | 	if (inplace) { | 
 | 285 | 		/* We're being asked to be a fold-in-place. Check | 
 | 286 | 		   that all blocks which actually have data associated | 
 | 287 | 		   with them (i.e. BlockMap[block] != BLOCK_NIL) are  | 
 | 288 | 		   either already present or SECTOR_FREE in the target | 
 | 289 | 		   block. If not, we're going to have to fold out-of-place | 
 | 290 | 		   anyway. | 
 | 291 | 		*/ | 
 | 292 | 		for (block = 0; block < nftl->EraseSize / 512 ; block++) { | 
 | 293 | 			if (BlockLastState[block] != SECTOR_FREE && | 
 | 294 | 			    BlockMap[block] != BLOCK_NIL && | 
 | 295 | 			    BlockMap[block] != targetEUN) { | 
 | 296 | 				DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, " | 
 | 297 | 				      "block %d was %x lastEUN, " | 
 | 298 | 				      "and is in EUN %d (%s) %d\n", | 
 | 299 | 				      thisVUC, block, BlockLastState[block], | 
 | 300 | 				      BlockMap[block],  | 
 | 301 | 				      BlockMap[block]== targetEUN ? "==" : "!=", | 
 | 302 | 				      targetEUN); | 
 | 303 | 				inplace = 0; | 
 | 304 | 				break; | 
 | 305 | 			} | 
 | 306 | 		} | 
 | 307 |  | 
 | 308 | 		if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) && | 
 | 309 | 		    pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) && | 
 | 310 | 		    BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] != | 
 | 311 | 		    SECTOR_FREE) { | 
 | 312 | 			DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. " | 
 | 313 | 			      "Folding out of place.\n", targetEUN); | 
 | 314 | 			inplace = 0; | 
 | 315 | 		} | 
 | 316 | 	} | 
 | 317 | 	 | 
 | 318 | 	if (!inplace) { | 
 | 319 | 		DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. " | 
 | 320 | 		      "Trying out-of-place\n", thisVUC); | 
 | 321 | 		/* We need to find a targetEUN to fold into. */ | 
 | 322 | 		targetEUN = NFTL_findfreeblock(nftl, 1); | 
 | 323 | 		if (targetEUN == BLOCK_NIL) { | 
 | 324 | 			/* Ouch. Now we're screwed. We need to do a  | 
 | 325 | 			   fold-in-place of another chain to make room | 
 | 326 | 			   for this one. We need a better way of selecting | 
 | 327 | 			   which chain to fold, because makefreeblock will  | 
 | 328 | 			   only ask us to fold the same one again. | 
 | 329 | 			*/ | 
 | 330 | 			printk(KERN_WARNING | 
 | 331 | 			       "NFTL_findfreeblock(desperate) returns 0xffff.\n"); | 
 | 332 | 			return BLOCK_NIL; | 
 | 333 | 		} | 
 | 334 | 	} else { | 
 | 335 |             /* We put a fold mark in the chain we are folding only if | 
 | 336 |                we fold in place to help the mount check code. If we do | 
 | 337 |                not fold in place, it is possible to find the valid | 
 | 338 |                chain by selecting the longer one */ | 
 | 339 |             oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); | 
 | 340 |             oob.u.c.unused = 0xffffffff; | 
 | 341 |             MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,  | 
 | 342 |                          8, &retlen, (char *)&oob.u); | 
 | 343 |         } | 
 | 344 |  | 
 | 345 | 	/* OK. We now know the location of every block in the Virtual Unit Chain, | 
 | 346 | 	   and the Erase Unit into which we are supposed to be copying. | 
 | 347 | 	   Go for it. | 
 | 348 | 	*/ | 
 | 349 | 	DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN); | 
 | 350 | 	for (block = 0; block < nftl->EraseSize / 512 ; block++) { | 
 | 351 | 		unsigned char movebuf[512]; | 
 | 352 | 		int ret; | 
 | 353 |  | 
 | 354 | 		/* If it's in the target EUN already, or if it's pending write, do nothing */ | 
 | 355 | 		if (BlockMap[block] == targetEUN || | 
 | 356 | 		    (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) { | 
 | 357 | 			continue; | 
 | 358 | 		} | 
 | 359 |  | 
 | 360 |                 /* copy only in non free block (free blocks can only | 
 | 361 |                    happen in case of media errors or deleted blocks) */ | 
 | 362 |                 if (BlockMap[block] == BLOCK_NIL) | 
 | 363 |                         continue; | 
 | 364 |                  | 
 | 365 |                 ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), | 
 | 366 | 				  512, &retlen, movebuf);  | 
 | 367 |                 if (ret < 0) { | 
 | 368 |                     ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) | 
 | 369 |                                       + (block * 512), 512, &retlen, | 
 | 370 |                                       movebuf);  | 
 | 371 |                     if (ret != -EIO)  | 
 | 372 |                         printk("Error went away on retry.\n"); | 
 | 373 |                 } | 
 | 374 | 		memset(&oob, 0xff, sizeof(struct nftl_oob)); | 
 | 375 | 		oob.b.Status = oob.b.Status1 = SECTOR_USED; | 
 | 376 |                 MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512), | 
 | 377 |                              512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo); | 
 | 378 | 	} | 
 | 379 |          | 
 | 380 |         /* add the header so that it is now a valid chain */ | 
 | 381 |         oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum | 
 | 382 |                 = cpu_to_le16(thisVUC); | 
 | 383 |         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; | 
 | 384 |          | 
 | 385 |         MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8,  | 
 | 386 |                      8, &retlen, (char *)&oob.u); | 
 | 387 |  | 
 | 388 | 	/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ | 
 | 389 |  | 
 | 390 | 	/* At this point, we have two different chains for this Virtual Unit, and no way to tell  | 
 | 391 | 	   them apart. If we crash now, we get confused. However, both contain the same data, so we | 
 | 392 | 	   shouldn't actually lose data in this case. It's just that when we load up on a medium which | 
 | 393 | 	   has duplicate chains, we need to free one of the chains because it's not necessary any more. | 
 | 394 | 	*/ | 
 | 395 | 	thisEUN = nftl->EUNtable[thisVUC]; | 
 | 396 | 	DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n"); | 
 | 397 |  | 
 | 398 | 	/* For each block in the old chain (except the targetEUN of course),  | 
 | 399 | 	   free it and make it available for future use */ | 
 | 400 | 	while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) { | 
 | 401 | 		unsigned int EUNtmp; | 
 | 402 |  | 
 | 403 |                 EUNtmp = nftl->ReplUnitTable[thisEUN]; | 
 | 404 |  | 
 | 405 |                 if (NFTL_formatblock(nftl, thisEUN) < 0) { | 
 | 406 | 			/* could not erase : mark block as reserved | 
 | 407 | 			 */ | 
 | 408 | 			nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; | 
 | 409 |                 } else { | 
 | 410 | 			/* correctly erased : mark it as free */ | 
 | 411 | 			nftl->ReplUnitTable[thisEUN] = BLOCK_FREE; | 
 | 412 | 			nftl->numfreeEUNs++; | 
 | 413 |                 } | 
 | 414 |                 thisEUN = EUNtmp; | 
 | 415 | 	} | 
 | 416 | 	 | 
 | 417 | 	/* Make this the new start of chain for thisVUC */ | 
 | 418 | 	nftl->ReplUnitTable[targetEUN] = BLOCK_NIL; | 
 | 419 | 	nftl->EUNtable[thisVUC] = targetEUN; | 
 | 420 |  | 
 | 421 | 	return targetEUN; | 
 | 422 | } | 
 | 423 |  | 
 | 424 | static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock) | 
 | 425 | { | 
 | 426 | 	/* This is the part that needs some cleverness applied.  | 
 | 427 | 	   For now, I'm doing the minimum applicable to actually | 
 | 428 | 	   get the thing to work. | 
 | 429 | 	   Wear-levelling and other clever stuff needs to be implemented | 
 | 430 | 	   and we also need to do some assessment of the results when | 
 | 431 | 	   the system loses power half-way through the routine. | 
 | 432 | 	*/ | 
 | 433 | 	u16 LongestChain = 0; | 
 | 434 | 	u16 ChainLength = 0, thislen; | 
 | 435 | 	u16 chain, EUN; | 
 | 436 |  | 
 | 437 | 	for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) { | 
 | 438 | 		EUN = nftl->EUNtable[chain]; | 
 | 439 | 		thislen = 0; | 
 | 440 |  | 
 | 441 | 		while (EUN <= nftl->lastEUN) { | 
 | 442 | 			thislen++; | 
 | 443 | 			//printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN); | 
 | 444 | 			EUN = nftl->ReplUnitTable[EUN] & 0x7fff; | 
 | 445 | 			if (thislen > 0xff00) { | 
 | 446 | 				printk("Endless loop in Virtual Chain %d: Unit %x\n", | 
 | 447 | 				       chain, EUN); | 
 | 448 | 			} | 
 | 449 | 			if (thislen > 0xff10) { | 
 | 450 | 				/* Actually, don't return failure. Just ignore this chain and | 
 | 451 | 				   get on with it. */ | 
 | 452 | 				thislen = 0; | 
 | 453 | 				break; | 
 | 454 | 			} | 
 | 455 | 		} | 
 | 456 |  | 
 | 457 | 		if (thislen > ChainLength) { | 
 | 458 | 			//printk("New longest chain is %d with length %d\n", chain, thislen); | 
 | 459 | 			ChainLength = thislen; | 
 | 460 | 			LongestChain = chain; | 
 | 461 | 		} | 
 | 462 | 	} | 
 | 463 |  | 
 | 464 | 	if (ChainLength < 2) { | 
 | 465 | 		printk(KERN_WARNING "No Virtual Unit Chains available for folding. " | 
 | 466 | 		       "Failing request\n"); | 
 | 467 | 		return 0xffff; | 
 | 468 | 	} | 
 | 469 |  | 
 | 470 | 	return NFTL_foldchain (nftl, LongestChain, pendingblock); | 
 | 471 | } | 
 | 472 |  | 
 | 473 | /* NFTL_findwriteunit: Return the unit number into which we can write  | 
 | 474 |                        for this block. Make it available if it isn't already | 
 | 475 | */ | 
 | 476 | static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) | 
 | 477 | { | 
 | 478 | 	u16 lastEUN; | 
 | 479 | 	u16 thisVUC = block / (nftl->EraseSize / 512); | 
 | 480 | 	unsigned int writeEUN; | 
 | 481 | 	unsigned long blockofs = (block * 512) & (nftl->EraseSize -1); | 
 | 482 | 	size_t retlen; | 
 | 483 | 	int silly, silly2 = 3; | 
 | 484 | 	struct nftl_oob oob; | 
 | 485 |  | 
 | 486 | 	do { | 
 | 487 | 		/* Scan the media to find a unit in the VUC which has | 
 | 488 | 		   a free space for the block in question. | 
 | 489 | 		*/ | 
 | 490 |  | 
 | 491 | 		/* This condition catches the 0x[7f]fff cases, as well as  | 
 | 492 | 		   being a sanity check for past-end-of-media access | 
 | 493 | 		*/ | 
 | 494 | 		lastEUN = BLOCK_NIL; | 
 | 495 | 		writeEUN = nftl->EUNtable[thisVUC]; | 
 | 496 |                 silly = MAX_LOOPS; | 
 | 497 | 		while (writeEUN <= nftl->lastEUN) { | 
 | 498 | 			struct nftl_bci bci; | 
 | 499 | 			size_t retlen; | 
 | 500 |                         unsigned int status; | 
 | 501 |  | 
 | 502 | 			lastEUN = writeEUN; | 
 | 503 |  | 
 | 504 | 			MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, | 
 | 505 | 				    8, &retlen, (char *)&bci); | 
 | 506 | 			 | 
 | 507 | 			DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n", | 
 | 508 | 			      block , writeEUN, le16_to_cpu(bci.Status)); | 
 | 509 |  | 
 | 510 |                         status = bci.Status | bci.Status1; | 
 | 511 | 			switch(status) { | 
 | 512 | 			case SECTOR_FREE: | 
 | 513 | 				return writeEUN; | 
 | 514 |  | 
 | 515 | 			case SECTOR_DELETED: | 
 | 516 | 			case SECTOR_USED: | 
 | 517 | 			case SECTOR_IGNORE: | 
 | 518 | 				break; | 
 | 519 | 			default: | 
 | 520 | 				// Invalid block. Don't use it any more. Must implement. | 
 | 521 | 				break;			 | 
 | 522 | 			} | 
 | 523 | 			 | 
 | 524 | 			if (!silly--) {  | 
 | 525 | 				printk(KERN_WARNING | 
 | 526 | 				       "Infinite loop in Virtual Unit Chain 0x%x\n", | 
 | 527 | 				       thisVUC); | 
 | 528 | 				return 0xffff; | 
 | 529 | 			} | 
 | 530 |  | 
 | 531 | 			/* Skip to next block in chain */ | 
 | 532 | 			writeEUN = nftl->ReplUnitTable[writeEUN]; | 
 | 533 | 		} | 
 | 534 |  | 
 | 535 | 		/* OK. We didn't find one in the existing chain, or there  | 
 | 536 | 		   is no existing chain. */ | 
 | 537 |  | 
 | 538 | 		/* Try to find an already-free block */ | 
 | 539 | 		writeEUN = NFTL_findfreeblock(nftl, 0); | 
 | 540 |  | 
 | 541 | 		if (writeEUN == BLOCK_NIL) { | 
 | 542 | 			/* That didn't work - there were no free blocks just | 
 | 543 | 			   waiting to be picked up. We're going to have to fold | 
 | 544 | 			   a chain to make room. | 
 | 545 | 			*/ | 
 | 546 |  | 
 | 547 | 			/* First remember the start of this chain */ | 
 | 548 | 			//u16 startEUN = nftl->EUNtable[thisVUC]; | 
 | 549 | 			 | 
 | 550 | 			//printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC); | 
 | 551 | 			writeEUN = NFTL_makefreeblock(nftl, 0xffff); | 
 | 552 |  | 
 | 553 | 			if (writeEUN == BLOCK_NIL) { | 
 | 554 | 				/* OK, we accept that the above comment is  | 
 | 555 | 				   lying - there may have been free blocks | 
 | 556 | 				   last time we called NFTL_findfreeblock(), | 
 | 557 | 				   but they are reserved for when we're | 
 | 558 | 				   desperate. Well, now we're desperate. | 
 | 559 | 				*/ | 
 | 560 | 				DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC); | 
 | 561 | 				writeEUN = NFTL_findfreeblock(nftl, 1); | 
 | 562 | 			} | 
 | 563 | 			if (writeEUN == BLOCK_NIL) { | 
 | 564 | 				/* Ouch. This should never happen - we should | 
 | 565 | 				   always be able to make some room somehow.  | 
 | 566 | 				   If we get here, we've allocated more storage  | 
 | 567 | 				   space than actual media, or our makefreeblock | 
 | 568 | 				   routine is missing something. | 
 | 569 | 				*/ | 
 | 570 | 				printk(KERN_WARNING "Cannot make free space.\n"); | 
 | 571 | 				return BLOCK_NIL; | 
 | 572 | 			}			 | 
 | 573 | 			//printk("Restarting scan\n"); | 
 | 574 | 			lastEUN = BLOCK_NIL; | 
 | 575 | 			continue; | 
 | 576 | 		} | 
 | 577 |  | 
 | 578 | 		/* We've found a free block. Insert it into the chain. */ | 
 | 579 | 		 | 
 | 580 | 		if (lastEUN != BLOCK_NIL) { | 
 | 581 |                     thisVUC |= 0x8000; /* It's a replacement block */ | 
 | 582 | 		} else { | 
 | 583 |                     /* The first block in a new chain */ | 
 | 584 |                     nftl->EUNtable[thisVUC] = writeEUN; | 
 | 585 | 		} | 
 | 586 |  | 
 | 587 | 		/* set up the actual EUN we're writing into */ | 
 | 588 | 		/* Both in our cache... */ | 
 | 589 | 		nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; | 
 | 590 |  | 
 | 591 | 		/* ... and on the flash itself */ | 
 | 592 | 		MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, | 
 | 593 | 			    &retlen, (char *)&oob.u); | 
 | 594 |  | 
 | 595 | 		oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); | 
 | 596 |  | 
 | 597 | 		MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, | 
 | 598 |                              &retlen, (char *)&oob.u); | 
 | 599 |  | 
 | 600 |                 /* we link the new block to the chain only after the | 
 | 601 |                    block is ready. It avoids the case where the chain | 
 | 602 |                    could point to a free block */ | 
 | 603 |                 if (lastEUN != BLOCK_NIL) { | 
 | 604 | 			/* Both in our cache... */ | 
 | 605 | 			nftl->ReplUnitTable[lastEUN] = writeEUN; | 
 | 606 | 			/* ... and on the flash itself */ | 
 | 607 | 			MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, | 
 | 608 | 				    8, &retlen, (char *)&oob.u); | 
 | 609 |  | 
 | 610 | 			oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum | 
 | 611 | 				= cpu_to_le16(writeEUN); | 
 | 612 |  | 
 | 613 | 			MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, | 
 | 614 | 				     8, &retlen, (char *)&oob.u); | 
 | 615 | 		} | 
 | 616 |  | 
 | 617 | 		return writeEUN; | 
 | 618 |  | 
 | 619 | 	} while (silly2--); | 
 | 620 |  | 
 | 621 | 	printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", | 
 | 622 | 	       thisVUC); | 
 | 623 | 	return 0xffff; | 
 | 624 | } | 
 | 625 |  | 
 | 626 | static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, | 
 | 627 | 			   char *buffer) | 
 | 628 | { | 
 | 629 | 	struct NFTLrecord *nftl = (void *)mbd; | 
 | 630 | 	u16 writeEUN; | 
 | 631 | 	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); | 
 | 632 | 	size_t retlen; | 
 | 633 | 	struct nftl_oob oob; | 
 | 634 |  | 
 | 635 | 	writeEUN = NFTL_findwriteunit(nftl, block); | 
 | 636 |  | 
 | 637 | 	if (writeEUN == BLOCK_NIL) { | 
 | 638 | 		printk(KERN_WARNING | 
 | 639 | 		       "NFTL_writeblock(): Cannot find block to write to\n"); | 
 | 640 | 		/* If we _still_ haven't got a block to use, we're screwed */ | 
 | 641 | 		return 1; | 
 | 642 | 	} | 
 | 643 |  | 
 | 644 | 	memset(&oob, 0xff, sizeof(struct nftl_oob)); | 
 | 645 | 	oob.b.Status = oob.b.Status1 = SECTOR_USED; | 
 | 646 | 	MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, | 
 | 647 | 		     512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo); | 
 | 648 |         /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */ | 
 | 649 |  | 
 | 650 | 	return 0; | 
 | 651 | } | 
 | 652 | #endif /* CONFIG_NFTL_RW */ | 
 | 653 |  | 
 | 654 | static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, | 
 | 655 | 			  char *buffer) | 
 | 656 | { | 
 | 657 | 	struct NFTLrecord *nftl = (void *)mbd; | 
 | 658 | 	u16 lastgoodEUN; | 
 | 659 | 	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; | 
 | 660 | 	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); | 
 | 661 |         unsigned int status; | 
 | 662 | 	int silly = MAX_LOOPS; | 
 | 663 |         size_t retlen; | 
 | 664 |         struct nftl_bci bci; | 
 | 665 |  | 
 | 666 | 	lastgoodEUN = BLOCK_NIL; | 
 | 667 |  | 
 | 668 |         if (thisEUN != BLOCK_NIL) { | 
 | 669 | 		while (thisEUN < nftl->nb_blocks) { | 
 | 670 | 			if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs, | 
 | 671 | 					8, &retlen, (char *)&bci) < 0) | 
 | 672 | 				status = SECTOR_IGNORE; | 
 | 673 | 			else | 
 | 674 | 				status = bci.Status | bci.Status1; | 
 | 675 |  | 
 | 676 | 			switch (status) { | 
 | 677 | 			case SECTOR_FREE: | 
 | 678 | 				/* no modification of a sector should follow a free sector */ | 
 | 679 | 				goto the_end; | 
 | 680 | 			case SECTOR_DELETED: | 
 | 681 | 				lastgoodEUN = BLOCK_NIL; | 
 | 682 | 				break; | 
 | 683 | 			case SECTOR_USED: | 
 | 684 | 				lastgoodEUN = thisEUN; | 
 | 685 | 				break; | 
 | 686 | 			case SECTOR_IGNORE: | 
 | 687 | 				break; | 
 | 688 | 			default: | 
 | 689 | 				printk("Unknown status for block %ld in EUN %d: %x\n", | 
 | 690 | 				       block, thisEUN, status); | 
 | 691 | 				break; | 
 | 692 | 			} | 
 | 693 |  | 
 | 694 | 			if (!silly--) { | 
 | 695 | 				printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n", | 
 | 696 | 				       block / (nftl->EraseSize / 512)); | 
 | 697 | 				return 1; | 
 | 698 | 			} | 
 | 699 | 			thisEUN = nftl->ReplUnitTable[thisEUN]; | 
 | 700 | 		} | 
 | 701 |         } | 
 | 702 |  | 
 | 703 |  the_end: | 
 | 704 | 	if (lastgoodEUN == BLOCK_NIL) { | 
 | 705 | 		/* the requested block is not on the media, return all 0x00 */ | 
 | 706 | 		memset(buffer, 0, 512); | 
 | 707 | 	} else { | 
 | 708 | 		loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; | 
 | 709 | 		size_t retlen; | 
 | 710 | 		if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer)) | 
 | 711 | 			return -EIO; | 
 | 712 | 	} | 
 | 713 | 	return 0; | 
 | 714 | } | 
 | 715 |  | 
 | 716 | static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo) | 
 | 717 | { | 
 | 718 | 	struct NFTLrecord *nftl = (void *)dev; | 
 | 719 |  | 
 | 720 | 	geo->heads = nftl->heads; | 
 | 721 | 	geo->sectors = nftl->sectors; | 
 | 722 | 	geo->cylinders = nftl->cylinders; | 
 | 723 |  | 
 | 724 | 	return 0; | 
 | 725 | } | 
 | 726 |  | 
 | 727 | /**************************************************************************** | 
 | 728 |  * | 
 | 729 |  * Module stuff | 
 | 730 |  * | 
 | 731 |  ****************************************************************************/ | 
 | 732 |  | 
 | 733 |  | 
 | 734 | static struct mtd_blktrans_ops nftl_tr = { | 
 | 735 | 	.name		= "nftl", | 
 | 736 | 	.major		= NFTL_MAJOR, | 
 | 737 | 	.part_bits	= NFTL_PARTN_BITS, | 
 | 738 | 	.getgeo		= nftl_getgeo, | 
 | 739 | 	.readsect	= nftl_readblock, | 
 | 740 | #ifdef CONFIG_NFTL_RW | 
 | 741 | 	.writesect	= nftl_writeblock, | 
 | 742 | #endif | 
 | 743 | 	.add_mtd	= nftl_add_mtd, | 
 | 744 | 	.remove_dev	= nftl_remove_dev, | 
 | 745 | 	.owner		= THIS_MODULE, | 
 | 746 | }; | 
 | 747 |  | 
 | 748 | extern char nftlmountrev[]; | 
 | 749 |  | 
 | 750 | static int __init init_nftl(void) | 
 | 751 | { | 
 | 752 | 	printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.97 $, nftlmount.c %s\n", nftlmountrev); | 
 | 753 |  | 
 | 754 | 	return register_mtd_blktrans(&nftl_tr); | 
 | 755 | } | 
 | 756 |  | 
 | 757 | static void __exit cleanup_nftl(void) | 
 | 758 | { | 
 | 759 | 	deregister_mtd_blktrans(&nftl_tr); | 
 | 760 | } | 
 | 761 |  | 
 | 762 | module_init(init_nftl); | 
 | 763 | module_exit(cleanup_nftl); | 
 | 764 |  | 
 | 765 | MODULE_LICENSE("GPL"); | 
 | 766 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al."); | 
 | 767 | MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium"); |