blob: 359533b33d9b36e7ab921c058e379d41d7ca9563 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* Linux driver for NAND Flash Translation Layer */
2/* (c) 1999 Machine Vision Holdings, Inc. */
3/* Author: David Woodhouse <dwmw2@infradead.org> */
Thomas Gleixner97894cd2005-11-07 11:15:26 +00004/* $Id: nftlcore.c,v 1.98 2005/11/07 11:14:21 gleixner Exp $ */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005
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
41static 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74 if (NFTL_mount(nftl) < 0) {
75 printk(KERN_WARNING "NFTL: could not mount device\n");
76 kfree(nftl);
77 return;
78 }
79
80 /* OK, it's a new one. Set up all the data structures. */
81
82 /* Calculate geometry */
83 nftl->cylinders = 1024;
84 nftl->heads = 16;
85
86 temp = nftl->cylinders * nftl->heads;
87 nftl->sectors = nftl->mbd.size / temp;
88 if (nftl->mbd.size % temp) {
89 nftl->sectors++;
90 temp = nftl->cylinders * nftl->sectors;
91 nftl->heads = nftl->mbd.size / temp;
92
93 if (nftl->mbd.size % temp) {
94 nftl->heads++;
95 temp = nftl->heads * nftl->sectors;
96 nftl->cylinders = nftl->mbd.size / temp;
97 }
98 }
99
100 if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
101 /*
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000102 Oh no we don't have
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 mbd.size == heads * cylinders * sectors
104 */
105 printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
106 "match size of 0x%lx.\n", nftl->mbd.size);
107 printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
108 "(== 0x%lx sects)\n",
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000109 nftl->cylinders, nftl->heads , nftl->sectors,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 (long)nftl->cylinders * (long)nftl->heads *
111 (long)nftl->sectors );
112 }
113
114 if (add_mtd_blktrans_dev(&nftl->mbd)) {
Jesper Juhlfa671642005-11-07 01:01:27 -0800115 kfree(nftl->ReplUnitTable);
116 kfree(nftl->EUNtable);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 kfree(nftl);
118 return;
119 }
120#ifdef PSYCHO_DEBUG
121 printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
122#endif
123}
124
125static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
126{
127 struct NFTLrecord *nftl = (void *)dev;
128
129 DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
130
131 del_mtd_blktrans_dev(dev);
Jesper Juhlfa671642005-11-07 01:01:27 -0800132 kfree(nftl->ReplUnitTable);
133 kfree(nftl->EUNtable);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 kfree(nftl);
135}
136
137#ifdef CONFIG_NFTL_RW
138
139/* Actual NFTL access routines */
140/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
141 * when the give Virtual Unit Chain
142 */
143static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
144{
145 /* For a given Virtual Unit Chain: find or create a free block and
146 add it to the chain */
147 /* We're passed the number of the last EUN in the chain, to save us from
148 having to look it up again */
149 u16 pot = nftl->LastFreeEUN;
150 int silly = nftl->nb_blocks;
151
152 /* Normally, we force a fold to happen before we run out of free blocks completely */
153 if (!desperate && nftl->numfreeEUNs < 2) {
154 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
155 return 0xffff;
156 }
157
158 /* Scan for a free block */
159 do {
160 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
161 nftl->LastFreeEUN = pot;
162 nftl->numfreeEUNs--;
163 return pot;
164 }
165
166 /* This will probably point to the MediaHdr unit itself,
167 right at the beginning of the partition. But that unit
168 (and the backup unit too) should have the UCI set
169 up so that it's not selected for overwriting */
170 if (++pot > nftl->lastEUN)
171 pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
172
173 if (!silly--) {
174 printk("Argh! No free blocks found! LastFreeEUN = %d, "
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000175 "FirstEUN = %d\n", nftl->LastFreeEUN,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
177 return 0xffff;
178 }
179 } while (pot != nftl->LastFreeEUN);
180
181 return 0xffff;
182}
183
184static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
185{
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200186 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 u16 BlockMap[MAX_SECTORS_PER_UNIT];
188 unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
189 unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
190 unsigned int thisEUN;
191 int block;
192 int silly;
193 unsigned int targetEUN;
194 struct nftl_oob oob;
195 int inplace = 1;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200196 size_t retlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
198 memset(BlockMap, 0xff, sizeof(BlockMap));
199 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
200
201 thisEUN = nftl->EUNtable[thisVUC];
202
203 if (thisEUN == BLOCK_NIL) {
204 printk(KERN_WARNING "Trying to fold non-existent "
205 "Virtual Unit Chain %d!\n", thisVUC);
206 return BLOCK_NIL;
207 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000208
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 /* Scan to find the Erase Unit which holds the actual data for each
210 512-byte block within the Chain.
211 */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200212 silly = MAX_LOOPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 targetEUN = BLOCK_NIL;
214 while (thisEUN <= nftl->lastEUN ) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200215 unsigned int status, foldmark;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
217 targetEUN = thisEUN;
218 for (block = 0; block < nftl->EraseSize / 512; block ++) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200219 mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) +
220 (block * 512), 16 , &retlen,
221 (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 if (block == 2) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200223 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
224 if (foldmark == FOLD_MARK_IN_PROGRESS) {
225 DEBUG(MTD_DEBUG_LEVEL1,
226 "Write Inhibited on EUN %d\n", thisEUN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 inplace = 0;
228 } else {
229 /* There's no other reason not to do inplace,
230 except ones that come later. So we don't need
231 to preserve inplace */
232 inplace = 1;
233 }
234 }
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200235 status = oob.b.Status | oob.b.Status1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 BlockLastState[block] = status;
237
238 switch(status) {
239 case SECTOR_FREE:
240 BlockFreeFound[block] = 1;
241 break;
242
243 case SECTOR_USED:
244 if (!BlockFreeFound[block])
245 BlockMap[block] = thisEUN;
246 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000247 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 "SECTOR_USED found after SECTOR_FREE "
249 "in Virtual Unit Chain %d for block %d\n",
250 thisVUC, block);
251 break;
252 case SECTOR_DELETED:
253 if (!BlockFreeFound[block])
254 BlockMap[block] = BLOCK_NIL;
255 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000256 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 "SECTOR_DELETED found after SECTOR_FREE "
258 "in Virtual Unit Chain %d for block %d\n",
259 thisVUC, block);
260 break;
261
262 case SECTOR_IGNORE:
263 break;
264 default:
265 printk("Unknown status for block %d in EUN %d: %x\n",
266 block, thisEUN, status);
267 }
268 }
269
270 if (!silly--) {
271 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
272 thisVUC);
273 return BLOCK_NIL;
274 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000275
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 thisEUN = nftl->ReplUnitTable[thisEUN];
277 }
278
279 if (inplace) {
280 /* We're being asked to be a fold-in-place. Check
281 that all blocks which actually have data associated
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000282 with them (i.e. BlockMap[block] != BLOCK_NIL) are
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 either already present or SECTOR_FREE in the target
284 block. If not, we're going to have to fold out-of-place
285 anyway.
286 */
287 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
288 if (BlockLastState[block] != SECTOR_FREE &&
289 BlockMap[block] != BLOCK_NIL &&
290 BlockMap[block] != targetEUN) {
291 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
292 "block %d was %x lastEUN, "
293 "and is in EUN %d (%s) %d\n",
294 thisVUC, block, BlockLastState[block],
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000295 BlockMap[block],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 BlockMap[block]== targetEUN ? "==" : "!=",
297 targetEUN);
298 inplace = 0;
299 break;
300 }
301 }
302
303 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
304 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
305 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
306 SECTOR_FREE) {
307 DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
308 "Folding out of place.\n", targetEUN);
309 inplace = 0;
310 }
311 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 if (!inplace) {
314 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
315 "Trying out-of-place\n", thisVUC);
316 /* We need to find a targetEUN to fold into. */
317 targetEUN = NFTL_findfreeblock(nftl, 1);
318 if (targetEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000319 /* Ouch. Now we're screwed. We need to do a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 fold-in-place of another chain to make room
321 for this one. We need a better way of selecting
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000322 which chain to fold, because makefreeblock will
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 only ask us to fold the same one again.
324 */
325 printk(KERN_WARNING
326 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
327 return BLOCK_NIL;
328 }
329 } else {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200330 /* We put a fold mark in the chain we are folding only if we
331 fold in place to help the mount check code. If we do not fold in
332 place, it is possible to find the valid chain by selecting the
333 longer one */
334 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
335 oob.u.c.unused = 0xffffffff;
336 mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
337 8, &retlen, (char *)&oob.u);
338 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340 /* OK. We now know the location of every block in the Virtual Unit Chain,
341 and the Erase Unit into which we are supposed to be copying.
342 Go for it.
343 */
344 DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
345 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
346 unsigned char movebuf[512];
347 int ret;
348
349 /* If it's in the target EUN already, or if it's pending write, do nothing */
350 if (BlockMap[block] == targetEUN ||
351 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
352 continue;
353 }
354
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200355 /* copy only in non free block (free blocks can only
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 happen in case of media errors or deleted blocks) */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200357 if (BlockMap[block] == BLOCK_NIL)
358 continue;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000359
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200360 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
361 512, &retlen, movebuf);
362 if (ret < 0) {
363 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
364 + (block * 512), 512, &retlen,
365 movebuf);
366 if (ret != -EIO)
367 printk("Error went away on retry.\n");
368 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 memset(&oob, 0xff, sizeof(struct nftl_oob));
370 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Thomas Gleixner9223a452006-05-23 17:21:03 +0200371
372 nand_write_raw(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
373 (block * 512), 512, &retlen, movebuf,
374 (char *)&oob);
375
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000377
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200378 /* add the header so that it is now a valid chain */
379 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
380 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000381
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200382 mtd->write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
383 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385 /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
386
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000387 /* At this point, we have two different chains for this Virtual Unit, and no way to tell
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 them apart. If we crash now, we get confused. However, both contain the same data, so we
389 shouldn't actually lose data in this case. It's just that when we load up on a medium which
390 has duplicate chains, we need to free one of the chains because it's not necessary any more.
391 */
392 thisEUN = nftl->EUNtable[thisVUC];
393 DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
394
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000395 /* For each block in the old chain (except the targetEUN of course),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 free it and make it available for future use */
397 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
398 unsigned int EUNtmp;
399
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200400 EUNtmp = nftl->ReplUnitTable[thisEUN];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200402 if (NFTL_formatblock(nftl, thisEUN) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 /* could not erase : mark block as reserved
404 */
405 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200406 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 /* correctly erased : mark it as free */
408 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
409 nftl->numfreeEUNs++;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200410 }
411 thisEUN = EUNtmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000413
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 /* Make this the new start of chain for thisVUC */
415 nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
416 nftl->EUNtable[thisVUC] = targetEUN;
417
418 return targetEUN;
419}
420
421static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
422{
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000423 /* This is the part that needs some cleverness applied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 For now, I'm doing the minimum applicable to actually
425 get the thing to work.
426 Wear-levelling and other clever stuff needs to be implemented
427 and we also need to do some assessment of the results when
428 the system loses power half-way through the routine.
429 */
430 u16 LongestChain = 0;
431 u16 ChainLength = 0, thislen;
432 u16 chain, EUN;
433
434 for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
435 EUN = nftl->EUNtable[chain];
436 thislen = 0;
437
438 while (EUN <= nftl->lastEUN) {
439 thislen++;
440 //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
441 EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
442 if (thislen > 0xff00) {
443 printk("Endless loop in Virtual Chain %d: Unit %x\n",
444 chain, EUN);
445 }
446 if (thislen > 0xff10) {
447 /* Actually, don't return failure. Just ignore this chain and
448 get on with it. */
449 thislen = 0;
450 break;
451 }
452 }
453
454 if (thislen > ChainLength) {
455 //printk("New longest chain is %d with length %d\n", chain, thislen);
456 ChainLength = thislen;
457 LongestChain = chain;
458 }
459 }
460
461 if (ChainLength < 2) {
462 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
463 "Failing request\n");
464 return 0xffff;
465 }
466
467 return NFTL_foldchain (nftl, LongestChain, pendingblock);
468}
469
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000470/* NFTL_findwriteunit: Return the unit number into which we can write
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 for this block. Make it available if it isn't already
472*/
473static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
474{
475 u16 lastEUN;
476 u16 thisVUC = block / (nftl->EraseSize / 512);
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200477 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 unsigned int writeEUN;
479 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
480 size_t retlen;
481 int silly, silly2 = 3;
482 struct nftl_oob oob;
483
484 do {
485 /* Scan the media to find a unit in the VUC which has
486 a free space for the block in question.
487 */
488
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000489 /* This condition catches the 0x[7f]fff cases, as well as
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 being a sanity check for past-end-of-media access
491 */
492 lastEUN = BLOCK_NIL;
493 writeEUN = nftl->EUNtable[thisVUC];
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200494 silly = MAX_LOOPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 while (writeEUN <= nftl->lastEUN) {
496 struct nftl_bci bci;
497 size_t retlen;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200498 unsigned int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
500 lastEUN = writeEUN;
501
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200502 mtd->read_oob(mtd,
503 (writeEUN * nftl->EraseSize) + blockofs,
504 8, &retlen, (char *)&bci);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000505
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
507 block , writeEUN, le16_to_cpu(bci.Status));
508
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200509 status = bci.Status | bci.Status1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 switch(status) {
511 case SECTOR_FREE:
512 return writeEUN;
513
514 case SECTOR_DELETED:
515 case SECTOR_USED:
516 case SECTOR_IGNORE:
517 break;
518 default:
519 // Invalid block. Don't use it any more. Must implement.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000520 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000522
523 if (!silly--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 printk(KERN_WARNING
525 "Infinite loop in Virtual Unit Chain 0x%x\n",
526 thisVUC);
527 return 0xffff;
528 }
529
530 /* Skip to next block in chain */
531 writeEUN = nftl->ReplUnitTable[writeEUN];
532 }
533
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000534 /* OK. We didn't find one in the existing chain, or there
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 is no existing chain. */
536
537 /* Try to find an already-free block */
538 writeEUN = NFTL_findfreeblock(nftl, 0);
539
540 if (writeEUN == BLOCK_NIL) {
541 /* That didn't work - there were no free blocks just
542 waiting to be picked up. We're going to have to fold
543 a chain to make room.
544 */
545
546 /* First remember the start of this chain */
547 //u16 startEUN = nftl->EUNtable[thisVUC];
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000548
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
550 writeEUN = NFTL_makefreeblock(nftl, 0xffff);
551
552 if (writeEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000553 /* OK, we accept that the above comment is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 lying - there may have been free blocks
555 last time we called NFTL_findfreeblock(),
556 but they are reserved for when we're
557 desperate. Well, now we're desperate.
558 */
559 DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
560 writeEUN = NFTL_findfreeblock(nftl, 1);
561 }
562 if (writeEUN == BLOCK_NIL) {
563 /* Ouch. This should never happen - we should
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000564 always be able to make some room somehow.
565 If we get here, we've allocated more storage
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 space than actual media, or our makefreeblock
567 routine is missing something.
568 */
569 printk(KERN_WARNING "Cannot make free space.\n");
570 return BLOCK_NIL;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 //printk("Restarting scan\n");
573 lastEUN = BLOCK_NIL;
574 continue;
575 }
576
577 /* We've found a free block. Insert it into the chain. */
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 if (lastEUN != BLOCK_NIL) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200580 thisVUC |= 0x8000; /* It's a replacement block */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 } else {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200582 /* The first block in a new chain */
583 nftl->EUNtable[thisVUC] = writeEUN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 }
585
586 /* set up the actual EUN we're writing into */
587 /* Both in our cache... */
588 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
589
590 /* ... and on the flash itself */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200591 mtd->read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
592 &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
594 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
595
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200596 mtd->write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
597 &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200599 /* we link the new block to the chain only after the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 block is ready. It avoids the case where the chain
601 could point to a free block */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200602 if (lastEUN != BLOCK_NIL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 /* Both in our cache... */
604 nftl->ReplUnitTable[lastEUN] = writeEUN;
605 /* ... and on the flash itself */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200606 mtd->read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
607 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
609 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
610 = cpu_to_le16(writeEUN);
611
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200612 mtd->write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
613 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 }
615
616 return writeEUN;
617
618 } while (silly2--);
619
620 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
621 thisVUC);
622 return 0xffff;
623}
624
625static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
626 char *buffer)
627{
628 struct NFTLrecord *nftl = (void *)mbd;
629 u16 writeEUN;
630 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
631 size_t retlen;
632 struct nftl_oob oob;
633
634 writeEUN = NFTL_findwriteunit(nftl, block);
635
636 if (writeEUN == BLOCK_NIL) {
637 printk(KERN_WARNING
638 "NFTL_writeblock(): Cannot find block to write to\n");
639 /* If we _still_ haven't got a block to use, we're screwed */
640 return 1;
641 }
642
643 memset(&oob, 0xff, sizeof(struct nftl_oob));
644 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
Thomas Gleixner9223a452006-05-23 17:21:03 +0200646 nand_write_raw(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) +
647 blockofs, 512, &retlen, (char *)buffer,
648 (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 return 0;
650}
651#endif /* CONFIG_NFTL_RW */
652
653static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
654 char *buffer)
655{
656 struct NFTLrecord *nftl = (void *)mbd;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200657 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 u16 lastgoodEUN;
659 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
660 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200661 unsigned int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 int silly = MAX_LOOPS;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200663 size_t retlen;
664 struct nftl_bci bci;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
666 lastgoodEUN = BLOCK_NIL;
667
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200668 if (thisEUN != BLOCK_NIL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 while (thisEUN < nftl->nb_blocks) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200670 if (mtd->read_oob(mtd, (thisEUN * nftl->EraseSize) +
671 blockofs, 8, &retlen,
672 (char *)&bci) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 status = SECTOR_IGNORE;
674 else
675 status = bci.Status | bci.Status1;
676
677 switch (status) {
678 case SECTOR_FREE:
679 /* no modification of a sector should follow a free sector */
680 goto the_end;
681 case SECTOR_DELETED:
682 lastgoodEUN = BLOCK_NIL;
683 break;
684 case SECTOR_USED:
685 lastgoodEUN = thisEUN;
686 break;
687 case SECTOR_IGNORE:
688 break;
689 default:
690 printk("Unknown status for block %ld in EUN %d: %x\n",
691 block, thisEUN, status);
692 break;
693 }
694
695 if (!silly--) {
696 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
697 block / (nftl->EraseSize / 512));
698 return 1;
699 }
700 thisEUN = nftl->ReplUnitTable[thisEUN];
701 }
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200702 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
704 the_end:
705 if (lastgoodEUN == BLOCK_NIL) {
706 /* the requested block is not on the media, return all 0x00 */
707 memset(buffer, 0, 512);
708 } else {
709 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
710 size_t retlen;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200711 if (mtd->read(mtd, ptr, 512, &retlen, buffer))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 return -EIO;
713 }
714 return 0;
715}
716
717static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
718{
719 struct NFTLrecord *nftl = (void *)dev;
720
721 geo->heads = nftl->heads;
722 geo->sectors = nftl->sectors;
723 geo->cylinders = nftl->cylinders;
724
725 return 0;
726}
727
728/****************************************************************************
729 *
730 * Module stuff
731 *
732 ****************************************************************************/
733
734
735static struct mtd_blktrans_ops nftl_tr = {
736 .name = "nftl",
737 .major = NFTL_MAJOR,
738 .part_bits = NFTL_PARTN_BITS,
739 .getgeo = nftl_getgeo,
740 .readsect = nftl_readblock,
741#ifdef CONFIG_NFTL_RW
742 .writesect = nftl_writeblock,
743#endif
744 .add_mtd = nftl_add_mtd,
745 .remove_dev = nftl_remove_dev,
746 .owner = THIS_MODULE,
747};
748
749extern char nftlmountrev[];
750
751static int __init init_nftl(void)
752{
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000753 printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
755 return register_mtd_blktrans(&nftl_tr);
756}
757
758static void __exit cleanup_nftl(void)
759{
760 deregister_mtd_blktrans(&nftl_tr);
761}
762
763module_init(init_nftl);
764module_exit(cleanup_nftl);
765
766MODULE_LICENSE("GPL");
767MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
768MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");