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