blob: dd03349946c26c8796c197fe1b6703bf0225fccf [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{
186 u16 BlockMap[MAX_SECTORS_PER_UNIT];
187 unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
188 unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
189 unsigned int thisEUN;
190 int block;
191 int silly;
192 unsigned int targetEUN;
193 struct nftl_oob oob;
194 int inplace = 1;
195 size_t retlen;
196
197 memset(BlockMap, 0xff, sizeof(BlockMap));
198 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
199
200 thisEUN = nftl->EUNtable[thisVUC];
201
202 if (thisEUN == BLOCK_NIL) {
203 printk(KERN_WARNING "Trying to fold non-existent "
204 "Virtual Unit Chain %d!\n", thisVUC);
205 return BLOCK_NIL;
206 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000207
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 /* Scan to find the Erase Unit which holds the actual data for each
209 512-byte block within the Chain.
210 */
211 silly = MAX_LOOPS;
212 targetEUN = BLOCK_NIL;
213 while (thisEUN <= nftl->lastEUN ) {
214 unsigned int status, foldmark;
215
216 targetEUN = thisEUN;
217 for (block = 0; block < nftl->EraseSize / 512; block ++) {
218 MTD_READOOB(nftl->mbd.mtd,
219 (thisEUN * nftl->EraseSize) + (block * 512),
220 16 , &retlen, (char *)&oob);
221 if (block == 2) {
222 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
223 if (foldmark == FOLD_MARK_IN_PROGRESS) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000224 DEBUG(MTD_DEBUG_LEVEL1,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 "Write Inhibited on EUN %d\n", thisEUN);
226 inplace = 0;
227 } else {
228 /* There's no other reason not to do inplace,
229 except ones that come later. So we don't need
230 to preserve inplace */
231 inplace = 1;
232 }
233 }
234 status = oob.b.Status | oob.b.Status1;
235 BlockLastState[block] = status;
236
237 switch(status) {
238 case SECTOR_FREE:
239 BlockFreeFound[block] = 1;
240 break;
241
242 case SECTOR_USED:
243 if (!BlockFreeFound[block])
244 BlockMap[block] = thisEUN;
245 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000246 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 "SECTOR_USED found after SECTOR_FREE "
248 "in Virtual Unit Chain %d for block %d\n",
249 thisVUC, block);
250 break;
251 case SECTOR_DELETED:
252 if (!BlockFreeFound[block])
253 BlockMap[block] = BLOCK_NIL;
254 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000255 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 "SECTOR_DELETED found after SECTOR_FREE "
257 "in Virtual Unit Chain %d for block %d\n",
258 thisVUC, block);
259 break;
260
261 case SECTOR_IGNORE:
262 break;
263 default:
264 printk("Unknown status for block %d in EUN %d: %x\n",
265 block, thisEUN, status);
266 }
267 }
268
269 if (!silly--) {
270 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
271 thisVUC);
272 return BLOCK_NIL;
273 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000274
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 thisEUN = nftl->ReplUnitTable[thisEUN];
276 }
277
278 if (inplace) {
279 /* We're being asked to be a fold-in-place. Check
280 that all blocks which actually have data associated
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000281 with them (i.e. BlockMap[block] != BLOCK_NIL) are
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 either already present or SECTOR_FREE in the target
283 block. If not, we're going to have to fold out-of-place
284 anyway.
285 */
286 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
287 if (BlockLastState[block] != SECTOR_FREE &&
288 BlockMap[block] != BLOCK_NIL &&
289 BlockMap[block] != targetEUN) {
290 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
291 "block %d was %x lastEUN, "
292 "and is in EUN %d (%s) %d\n",
293 thisVUC, block, BlockLastState[block],
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000294 BlockMap[block],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 BlockMap[block]== targetEUN ? "==" : "!=",
296 targetEUN);
297 inplace = 0;
298 break;
299 }
300 }
301
302 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
303 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
304 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
305 SECTOR_FREE) {
306 DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
307 "Folding out of place.\n", targetEUN);
308 inplace = 0;
309 }
310 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 if (!inplace) {
313 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
314 "Trying out-of-place\n", thisVUC);
315 /* We need to find a targetEUN to fold into. */
316 targetEUN = NFTL_findfreeblock(nftl, 1);
317 if (targetEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000318 /* Ouch. Now we're screwed. We need to do a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 fold-in-place of another chain to make room
320 for this one. We need a better way of selecting
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000321 which chain to fold, because makefreeblock will
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 only ask us to fold the same one again.
323 */
324 printk(KERN_WARNING
325 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
326 return BLOCK_NIL;
327 }
328 } else {
329 /* We put a fold mark in the chain we are folding only if
330 we fold in place to help the mount check code. If we do
331 not fold in place, it is possible to find the valid
332 chain by selecting the longer one */
333 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
334 oob.u.c.unused = 0xffffffff;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000335 MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 8, &retlen, (char *)&oob.u);
337 }
338
339 /* OK. We now know the location of every block in the Virtual Unit Chain,
340 and the Erase Unit into which we are supposed to be copying.
341 Go for it.
342 */
343 DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
344 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
345 unsigned char movebuf[512];
346 int ret;
347
348 /* If it's in the target EUN already, or if it's pending write, do nothing */
349 if (BlockMap[block] == targetEUN ||
350 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
351 continue;
352 }
353
354 /* copy only in non free block (free blocks can only
355 happen in case of media errors or deleted blocks) */
356 if (BlockMap[block] == BLOCK_NIL)
357 continue;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000360 512, &retlen, movebuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 if (ret < 0) {
362 ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
363 + (block * 512), 512, &retlen,
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000364 movebuf);
365 if (ret != -EIO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 printk("Error went away on retry.\n");
367 }
368 memset(&oob, 0xff, sizeof(struct nftl_oob));
369 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Thomas Gleixner9223a452006-05-23 17:21:03 +0200370
371 nand_write_raw(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
372 (block * 512), 512, &retlen, movebuf,
373 (char *)&oob);
374
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000376
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 /* add the header so that it is now a valid chain */
378 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
379 = cpu_to_le16(thisVUC);
380 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000381
382 MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 8, &retlen, (char *)&oob.u);
384
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
400 EUNtmp = nftl->ReplUnitTable[thisEUN];
401
402 if (NFTL_formatblock(nftl, thisEUN) < 0) {
403 /* could not erase : mark block as reserved
404 */
405 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
406 } else {
407 /* correctly erased : mark it as free */
408 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
409 nftl->numfreeEUNs++;
410 }
411 thisEUN = EUNtmp;
412 }
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);
477 unsigned int writeEUN;
478 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
479 size_t retlen;
480 int silly, silly2 = 3;
481 struct nftl_oob oob;
482
483 do {
484 /* Scan the media to find a unit in the VUC which has
485 a free space for the block in question.
486 */
487
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000488 /* This condition catches the 0x[7f]fff cases, as well as
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 being a sanity check for past-end-of-media access
490 */
491 lastEUN = BLOCK_NIL;
492 writeEUN = nftl->EUNtable[thisVUC];
493 silly = MAX_LOOPS;
494 while (writeEUN <= nftl->lastEUN) {
495 struct nftl_bci bci;
496 size_t retlen;
497 unsigned int status;
498
499 lastEUN = writeEUN;
500
501 MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
502 8, &retlen, (char *)&bci);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000503
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
505 block , writeEUN, le16_to_cpu(bci.Status));
506
507 status = bci.Status | bci.Status1;
508 switch(status) {
509 case SECTOR_FREE:
510 return writeEUN;
511
512 case SECTOR_DELETED:
513 case SECTOR_USED:
514 case SECTOR_IGNORE:
515 break;
516 default:
517 // Invalid block. Don't use it any more. Must implement.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000518 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000520
521 if (!silly--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 printk(KERN_WARNING
523 "Infinite loop in Virtual Unit Chain 0x%x\n",
524 thisVUC);
525 return 0xffff;
526 }
527
528 /* Skip to next block in chain */
529 writeEUN = nftl->ReplUnitTable[writeEUN];
530 }
531
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000532 /* OK. We didn't find one in the existing chain, or there
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 is no existing chain. */
534
535 /* Try to find an already-free block */
536 writeEUN = NFTL_findfreeblock(nftl, 0);
537
538 if (writeEUN == BLOCK_NIL) {
539 /* That didn't work - there were no free blocks just
540 waiting to be picked up. We're going to have to fold
541 a chain to make room.
542 */
543
544 /* First remember the start of this chain */
545 //u16 startEUN = nftl->EUNtable[thisVUC];
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
548 writeEUN = NFTL_makefreeblock(nftl, 0xffff);
549
550 if (writeEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000551 /* OK, we accept that the above comment is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 lying - there may have been free blocks
553 last time we called NFTL_findfreeblock(),
554 but they are reserved for when we're
555 desperate. Well, now we're desperate.
556 */
557 DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
558 writeEUN = NFTL_findfreeblock(nftl, 1);
559 }
560 if (writeEUN == BLOCK_NIL) {
561 /* Ouch. This should never happen - we should
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000562 always be able to make some room somehow.
563 If we get here, we've allocated more storage
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 space than actual media, or our makefreeblock
565 routine is missing something.
566 */
567 printk(KERN_WARNING "Cannot make free space.\n");
568 return BLOCK_NIL;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000569 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 //printk("Restarting scan\n");
571 lastEUN = BLOCK_NIL;
572 continue;
573 }
574
575 /* We've found a free block. Insert it into the chain. */
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000576
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 if (lastEUN != BLOCK_NIL) {
578 thisVUC |= 0x8000; /* It's a replacement block */
579 } else {
580 /* The first block in a new chain */
581 nftl->EUNtable[thisVUC] = writeEUN;
582 }
583
584 /* set up the actual EUN we're writing into */
585 /* Both in our cache... */
586 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
587
588 /* ... and on the flash itself */
589 MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
590 &retlen, (char *)&oob.u);
591
592 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
593
594 MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
595 &retlen, (char *)&oob.u);
596
597 /* we link the new block to the chain only after the
598 block is ready. It avoids the case where the chain
599 could point to a free block */
600 if (lastEUN != BLOCK_NIL) {
601 /* Both in our cache... */
602 nftl->ReplUnitTable[lastEUN] = writeEUN;
603 /* ... and on the flash itself */
604 MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
605 8, &retlen, (char *)&oob.u);
606
607 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
608 = cpu_to_le16(writeEUN);
609
610 MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
611 8, &retlen, (char *)&oob.u);
612 }
613
614 return writeEUN;
615
616 } while (silly2--);
617
618 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
619 thisVUC);
620 return 0xffff;
621}
622
623static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
624 char *buffer)
625{
626 struct NFTLrecord *nftl = (void *)mbd;
627 u16 writeEUN;
628 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
629 size_t retlen;
630 struct nftl_oob oob;
631
632 writeEUN = NFTL_findwriteunit(nftl, block);
633
634 if (writeEUN == BLOCK_NIL) {
635 printk(KERN_WARNING
636 "NFTL_writeblock(): Cannot find block to write to\n");
637 /* If we _still_ haven't got a block to use, we're screwed */
638 return 1;
639 }
640
641 memset(&oob, 0xff, sizeof(struct nftl_oob));
642 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643
Thomas Gleixner9223a452006-05-23 17:21:03 +0200644 nand_write_raw(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) +
645 blockofs, 512, &retlen, (char *)buffer,
646 (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 return 0;
648}
649#endif /* CONFIG_NFTL_RW */
650
651static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
652 char *buffer)
653{
654 struct NFTLrecord *nftl = (void *)mbd;
655 u16 lastgoodEUN;
656 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
657 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
658 unsigned int status;
659 int silly = MAX_LOOPS;
660 size_t retlen;
661 struct nftl_bci bci;
662
663 lastgoodEUN = BLOCK_NIL;
664
665 if (thisEUN != BLOCK_NIL) {
666 while (thisEUN < nftl->nb_blocks) {
667 if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs,
668 8, &retlen, (char *)&bci) < 0)
669 status = SECTOR_IGNORE;
670 else
671 status = bci.Status | bci.Status1;
672
673 switch (status) {
674 case SECTOR_FREE:
675 /* no modification of a sector should follow a free sector */
676 goto the_end;
677 case SECTOR_DELETED:
678 lastgoodEUN = BLOCK_NIL;
679 break;
680 case SECTOR_USED:
681 lastgoodEUN = thisEUN;
682 break;
683 case SECTOR_IGNORE:
684 break;
685 default:
686 printk("Unknown status for block %ld in EUN %d: %x\n",
687 block, thisEUN, status);
688 break;
689 }
690
691 if (!silly--) {
692 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
693 block / (nftl->EraseSize / 512));
694 return 1;
695 }
696 thisEUN = nftl->ReplUnitTable[thisEUN];
697 }
698 }
699
700 the_end:
701 if (lastgoodEUN == BLOCK_NIL) {
702 /* the requested block is not on the media, return all 0x00 */
703 memset(buffer, 0, 512);
704 } else {
705 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
706 size_t retlen;
707 if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer))
708 return -EIO;
709 }
710 return 0;
711}
712
713static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
714{
715 struct NFTLrecord *nftl = (void *)dev;
716
717 geo->heads = nftl->heads;
718 geo->sectors = nftl->sectors;
719 geo->cylinders = nftl->cylinders;
720
721 return 0;
722}
723
724/****************************************************************************
725 *
726 * Module stuff
727 *
728 ****************************************************************************/
729
730
731static struct mtd_blktrans_ops nftl_tr = {
732 .name = "nftl",
733 .major = NFTL_MAJOR,
734 .part_bits = NFTL_PARTN_BITS,
735 .getgeo = nftl_getgeo,
736 .readsect = nftl_readblock,
737#ifdef CONFIG_NFTL_RW
738 .writesect = nftl_writeblock,
739#endif
740 .add_mtd = nftl_add_mtd,
741 .remove_dev = nftl_remove_dev,
742 .owner = THIS_MODULE,
743};
744
745extern char nftlmountrev[];
746
747static int __init init_nftl(void)
748{
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000749 printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
751 return register_mtd_blktrans(&nftl_tr);
752}
753
754static void __exit cleanup_nftl(void)
755{
756 deregister_mtd_blktrans(&nftl_tr);
757}
758
759module_init(init_nftl);
760module_exit(cleanup_nftl);
761
762MODULE_LICENSE("GPL");
763MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
764MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");