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