tools headers UAPI: Sync linux/prctl.h with the kernel sources
[linux-2.6-microblaze.git] / drivers / mtd / nftlcore.c
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
35 static 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
109 static 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  */
123 int 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  */
144 int 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  */
167 static 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  */
190 static 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
231 static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
232 {
233         struct mtd_info *mtd = nftl->mbd.mtd;
234         u16 BlockMap[MAX_SECTORS_PER_UNIT];
235         unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
236         unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
237         unsigned int thisEUN;
238         int block;
239         int silly;
240         unsigned int targetEUN;
241         struct nftl_oob oob;
242         int inplace = 1;
243         size_t retlen;
244
245         memset(BlockMap, 0xff, sizeof(BlockMap));
246         memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
247
248         thisEUN = nftl->EUNtable[thisVUC];
249
250         if (thisEUN == BLOCK_NIL) {
251                 printk(KERN_WARNING "Trying to fold non-existent "
252                        "Virtual Unit Chain %d!\n", thisVUC);
253                 return BLOCK_NIL;
254         }
255
256         /* Scan to find the Erase Unit which holds the actual data for each
257            512-byte block within the Chain.
258         */
259         silly = MAX_LOOPS;
260         targetEUN = BLOCK_NIL;
261         while (thisEUN <= nftl->lastEUN ) {
262                 unsigned int status, foldmark;
263
264                 targetEUN = thisEUN;
265                 for (block = 0; block < nftl->EraseSize / 512; block ++) {
266                         nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
267                                       (block * 512), 16 , &retlen,
268                                       (char *)&oob);
269                         if (block == 2) {
270                                 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
271                                 if (foldmark == FOLD_MARK_IN_PROGRESS) {
272                                         pr_debug("Write Inhibited on EUN %d\n", thisEUN);
273                                         inplace = 0;
274                                 } else {
275                                         /* There's no other reason not to do inplace,
276                                            except ones that come later. So we don't need
277                                            to preserve inplace */
278                                         inplace = 1;
279                                 }
280                         }
281                         status = oob.b.Status | oob.b.Status1;
282                         BlockLastState[block] = status;
283
284                         switch(status) {
285                         case SECTOR_FREE:
286                                 BlockFreeFound[block] = 1;
287                                 break;
288
289                         case SECTOR_USED:
290                                 if (!BlockFreeFound[block])
291                                         BlockMap[block] = thisEUN;
292                                 else
293                                         printk(KERN_WARNING
294                                                "SECTOR_USED found after SECTOR_FREE "
295                                                "in Virtual Unit Chain %d for block %d\n",
296                                                thisVUC, block);
297                                 break;
298                         case SECTOR_DELETED:
299                                 if (!BlockFreeFound[block])
300                                         BlockMap[block] = BLOCK_NIL;
301                                 else
302                                         printk(KERN_WARNING
303                                                "SECTOR_DELETED found after SECTOR_FREE "
304                                                "in Virtual Unit Chain %d for block %d\n",
305                                                thisVUC, block);
306                                 break;
307
308                         case SECTOR_IGNORE:
309                                 break;
310                         default:
311                                 printk("Unknown status for block %d in EUN %d: %x\n",
312                                        block, thisEUN, status);
313                         }
314                 }
315
316                 if (!silly--) {
317                         printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
318                                thisVUC);
319                         return BLOCK_NIL;
320                 }
321
322                 thisEUN = nftl->ReplUnitTable[thisEUN];
323         }
324
325         if (inplace) {
326                 /* We're being asked to be a fold-in-place. Check
327                    that all blocks which actually have data associated
328                    with them (i.e. BlockMap[block] != BLOCK_NIL) are
329                    either already present or SECTOR_FREE in the target
330                    block. If not, we're going to have to fold out-of-place
331                    anyway.
332                 */
333                 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
334                         if (BlockLastState[block] != SECTOR_FREE &&
335                             BlockMap[block] != BLOCK_NIL &&
336                             BlockMap[block] != targetEUN) {
337                                 pr_debug("Setting inplace to 0. VUC %d, "
338                                       "block %d was %x lastEUN, "
339                                       "and is in EUN %d (%s) %d\n",
340                                       thisVUC, block, BlockLastState[block],
341                                       BlockMap[block],
342                                       BlockMap[block]== targetEUN ? "==" : "!=",
343                                       targetEUN);
344                                 inplace = 0;
345                                 break;
346                         }
347                 }
348
349                 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
350                     pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
351                     BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
352                     SECTOR_FREE) {
353                         pr_debug("Pending write not free in EUN %d. "
354                               "Folding out of place.\n", targetEUN);
355                         inplace = 0;
356                 }
357         }
358
359         if (!inplace) {
360                 pr_debug("Cannot fold Virtual Unit Chain %d in place. "
361                       "Trying out-of-place\n", thisVUC);
362                 /* We need to find a targetEUN to fold into. */
363                 targetEUN = NFTL_findfreeblock(nftl, 1);
364                 if (targetEUN == BLOCK_NIL) {
365                         /* Ouch. Now we're screwed. We need to do a
366                            fold-in-place of another chain to make room
367                            for this one. We need a better way of selecting
368                            which chain to fold, because makefreeblock will
369                            only ask us to fold the same one again.
370                         */
371                         printk(KERN_WARNING
372                                "NFTL_findfreeblock(desperate) returns 0xffff.\n");
373                         return BLOCK_NIL;
374                 }
375         } else {
376                 /* We put a fold mark in the chain we are folding only if we
377                fold in place to help the mount check code. If we do not fold in
378                place, it is possible to find the valid chain by selecting the
379                longer one */
380                 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
381                 oob.u.c.unused = 0xffffffff;
382                 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
383                                8, &retlen, (char *)&oob.u);
384         }
385
386         /* OK. We now know the location of every block in the Virtual Unit Chain,
387            and the Erase Unit into which we are supposed to be copying.
388            Go for it.
389         */
390         pr_debug("Folding chain %d into unit %d\n", thisVUC, targetEUN);
391         for (block = 0; block < nftl->EraseSize / 512 ; block++) {
392                 unsigned char movebuf[512];
393                 int ret;
394
395                 /* If it's in the target EUN already, or if it's pending write, do nothing */
396                 if (BlockMap[block] == targetEUN ||
397                     (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
398                         continue;
399                 }
400
401                 /* copy only in non free block (free blocks can only
402                    happen in case of media errors or deleted blocks) */
403                 if (BlockMap[block] == BLOCK_NIL)
404                         continue;
405
406                 ret = mtd_read(mtd,
407                                (nftl->EraseSize * BlockMap[block]) + (block * 512),
408                                512,
409                                &retlen,
410                                movebuf);
411                 if (ret < 0 && !mtd_is_bitflip(ret)) {
412                         ret = mtd_read(mtd,
413                                        (nftl->EraseSize * BlockMap[block]) + (block * 512),
414                                        512,
415                                        &retlen,
416                                        movebuf);
417                         if (ret != -EIO)
418                                 printk("Error went away on retry.\n");
419                 }
420                 memset(&oob, 0xff, sizeof(struct nftl_oob));
421                 oob.b.Status = oob.b.Status1 = SECTOR_USED;
422
423                 nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
424                            (block * 512), 512, &retlen, movebuf, (char *)&oob);
425         }
426
427         /* add the header so that it is now a valid chain */
428         oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
429         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = BLOCK_NIL;
430
431         nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
432                        8, &retlen, (char *)&oob.u);
433
434         /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
435
436         /* At this point, we have two different chains for this Virtual Unit, and no way to tell
437            them apart. If we crash now, we get confused. However, both contain the same data, so we
438            shouldn't actually lose data in this case. It's just that when we load up on a medium which
439            has duplicate chains, we need to free one of the chains because it's not necessary any more.
440         */
441         thisEUN = nftl->EUNtable[thisVUC];
442         pr_debug("Want to erase\n");
443
444         /* For each block in the old chain (except the targetEUN of course),
445            free it and make it available for future use */
446         while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
447                 unsigned int EUNtmp;
448
449                 EUNtmp = nftl->ReplUnitTable[thisEUN];
450
451                 if (NFTL_formatblock(nftl, thisEUN) < 0) {
452                         /* could not erase : mark block as reserved
453                          */
454                         nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
455                 } else {
456                         /* correctly erased : mark it as free */
457                         nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
458                         nftl->numfreeEUNs++;
459                 }
460                 thisEUN = EUNtmp;
461         }
462
463         /* Make this the new start of chain for thisVUC */
464         nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
465         nftl->EUNtable[thisVUC] = targetEUN;
466
467         return targetEUN;
468 }
469
470 static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
471 {
472         /* This is the part that needs some cleverness applied.
473            For now, I'm doing the minimum applicable to actually
474            get the thing to work.
475            Wear-levelling and other clever stuff needs to be implemented
476            and we also need to do some assessment of the results when
477            the system loses power half-way through the routine.
478         */
479         u16 LongestChain = 0;
480         u16 ChainLength = 0, thislen;
481         u16 chain, EUN;
482
483         for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
484                 EUN = nftl->EUNtable[chain];
485                 thislen = 0;
486
487                 while (EUN <= nftl->lastEUN) {
488                         thislen++;
489                         //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
490                         EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
491                         if (thislen > 0xff00) {
492                                 printk("Endless loop in Virtual Chain %d: Unit %x\n",
493                                        chain, EUN);
494                         }
495                         if (thislen > 0xff10) {
496                                 /* Actually, don't return failure. Just ignore this chain and
497                                    get on with it. */
498                                 thislen = 0;
499                                 break;
500                         }
501                 }
502
503                 if (thislen > ChainLength) {
504                         //printk("New longest chain is %d with length %d\n", chain, thislen);
505                         ChainLength = thislen;
506                         LongestChain = chain;
507                 }
508         }
509
510         if (ChainLength < 2) {
511                 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
512                        "Failing request\n");
513                 return BLOCK_NIL;
514         }
515
516         return NFTL_foldchain (nftl, LongestChain, pendingblock);
517 }
518
519 /* NFTL_findwriteunit: Return the unit number into which we can write
520                        for this block. Make it available if it isn't already
521 */
522 static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
523 {
524         u16 lastEUN;
525         u16 thisVUC = block / (nftl->EraseSize / 512);
526         struct mtd_info *mtd = nftl->mbd.mtd;
527         unsigned int writeEUN;
528         unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
529         size_t retlen;
530         int silly, silly2 = 3;
531         struct nftl_oob oob;
532
533         do {
534                 /* Scan the media to find a unit in the VUC which has
535                    a free space for the block in question.
536                 */
537
538                 /* This condition catches the 0x[7f]fff cases, as well as
539                    being a sanity check for past-end-of-media access
540                 */
541                 lastEUN = BLOCK_NIL;
542                 writeEUN = nftl->EUNtable[thisVUC];
543                 silly = MAX_LOOPS;
544                 while (writeEUN <= nftl->lastEUN) {
545                         struct nftl_bci bci;
546                         size_t retlen;
547                         unsigned int status;
548
549                         lastEUN = writeEUN;
550
551                         nftl_read_oob(mtd,
552                                       (writeEUN * nftl->EraseSize) + blockofs,
553                                       8, &retlen, (char *)&bci);
554
555                         pr_debug("Status of block %d in EUN %d is %x\n",
556                               block , writeEUN, le16_to_cpu(bci.Status));
557
558                         status = bci.Status | bci.Status1;
559                         switch(status) {
560                         case SECTOR_FREE:
561                                 return writeEUN;
562
563                         case SECTOR_DELETED:
564                         case SECTOR_USED:
565                         case SECTOR_IGNORE:
566                                 break;
567                         default:
568                                 // Invalid block. Don't use it any more. Must implement.
569                                 break;
570                         }
571
572                         if (!silly--) {
573                                 printk(KERN_WARNING
574                                        "Infinite loop in Virtual Unit Chain 0x%x\n",
575                                        thisVUC);
576                                 return BLOCK_NIL;
577                         }
578
579                         /* Skip to next block in chain */
580                         writeEUN = nftl->ReplUnitTable[writeEUN];
581                 }
582
583                 /* OK. We didn't find one in the existing chain, or there
584                    is no existing chain. */
585
586                 /* Try to find an already-free block */
587                 writeEUN = NFTL_findfreeblock(nftl, 0);
588
589                 if (writeEUN == BLOCK_NIL) {
590                         /* That didn't work - there were no free blocks just
591                            waiting to be picked up. We're going to have to fold
592                            a chain to make room.
593                         */
594
595                         /* First remember the start of this chain */
596                         //u16 startEUN = nftl->EUNtable[thisVUC];
597
598                         //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
599                         writeEUN = NFTL_makefreeblock(nftl, BLOCK_NIL);
600
601                         if (writeEUN == BLOCK_NIL) {
602                                 /* OK, we accept that the above comment is
603                                    lying - there may have been free blocks
604                                    last time we called NFTL_findfreeblock(),
605                                    but they are reserved for when we're
606                                    desperate. Well, now we're desperate.
607                                 */
608                                 pr_debug("Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
609                                 writeEUN = NFTL_findfreeblock(nftl, 1);
610                         }
611                         if (writeEUN == BLOCK_NIL) {
612                                 /* Ouch. This should never happen - we should
613                                    always be able to make some room somehow.
614                                    If we get here, we've allocated more storage
615                                    space than actual media, or our makefreeblock
616                                    routine is missing something.
617                                 */
618                                 printk(KERN_WARNING "Cannot make free space.\n");
619                                 return BLOCK_NIL;
620                         }
621                         //printk("Restarting scan\n");
622                         lastEUN = BLOCK_NIL;
623                         continue;
624                 }
625
626                 /* We've found a free block. Insert it into the chain. */
627
628                 if (lastEUN != BLOCK_NIL) {
629                         thisVUC |= 0x8000; /* It's a replacement block */
630                 } else {
631                         /* The first block in a new chain */
632                         nftl->EUNtable[thisVUC] = writeEUN;
633                 }
634
635                 /* set up the actual EUN we're writing into */
636                 /* Both in our cache... */
637                 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
638
639                 /* ... and on the flash itself */
640                 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
641                               &retlen, (char *)&oob.u);
642
643                 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
644
645                 nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
646                                &retlen, (char *)&oob.u);
647
648                 /* we link the new block to the chain only after the
649                    block is ready. It avoids the case where the chain
650                    could point to a free block */
651                 if (lastEUN != BLOCK_NIL) {
652                         /* Both in our cache... */
653                         nftl->ReplUnitTable[lastEUN] = writeEUN;
654                         /* ... and on the flash itself */
655                         nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
656                                       8, &retlen, (char *)&oob.u);
657
658                         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
659                                 = cpu_to_le16(writeEUN);
660
661                         nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
662                                        8, &retlen, (char *)&oob.u);
663                 }
664
665                 return writeEUN;
666
667         } while (silly2--);
668
669         printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
670                thisVUC);
671         return BLOCK_NIL;
672 }
673
674 static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
675                            char *buffer)
676 {
677         struct NFTLrecord *nftl = (void *)mbd;
678         u16 writeEUN;
679         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
680         size_t retlen;
681         struct nftl_oob oob;
682
683         writeEUN = NFTL_findwriteunit(nftl, block);
684
685         if (writeEUN == BLOCK_NIL) {
686                 printk(KERN_WARNING
687                        "NFTL_writeblock(): Cannot find block to write to\n");
688                 /* If we _still_ haven't got a block to use, we're screwed */
689                 return 1;
690         }
691
692         memset(&oob, 0xff, sizeof(struct nftl_oob));
693         oob.b.Status = oob.b.Status1 = SECTOR_USED;
694
695         nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
696                    512, &retlen, (char *)buffer, (char *)&oob);
697         return 0;
698 }
699 #endif /* CONFIG_NFTL_RW */
700
701 static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
702                           char *buffer)
703 {
704         struct NFTLrecord *nftl = (void *)mbd;
705         struct mtd_info *mtd = nftl->mbd.mtd;
706         u16 lastgoodEUN;
707         u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
708         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
709         unsigned int status;
710         int silly = MAX_LOOPS;
711         size_t retlen;
712         struct nftl_bci bci;
713
714         lastgoodEUN = BLOCK_NIL;
715
716         if (thisEUN != BLOCK_NIL) {
717                 while (thisEUN < nftl->nb_blocks) {
718                         if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
719                                           blockofs, 8, &retlen,
720                                           (char *)&bci) < 0)
721                                 status = SECTOR_IGNORE;
722                         else
723                                 status = bci.Status | bci.Status1;
724
725                         switch (status) {
726                         case SECTOR_FREE:
727                                 /* no modification of a sector should follow a free sector */
728                                 goto the_end;
729                         case SECTOR_DELETED:
730                                 lastgoodEUN = BLOCK_NIL;
731                                 break;
732                         case SECTOR_USED:
733                                 lastgoodEUN = thisEUN;
734                                 break;
735                         case SECTOR_IGNORE:
736                                 break;
737                         default:
738                                 printk("Unknown status for block %ld in EUN %d: %x\n",
739                                        block, thisEUN, status);
740                                 break;
741                         }
742
743                         if (!silly--) {
744                                 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
745                                        block / (nftl->EraseSize / 512));
746                                 return 1;
747                         }
748                         thisEUN = nftl->ReplUnitTable[thisEUN];
749                 }
750         }
751
752  the_end:
753         if (lastgoodEUN == BLOCK_NIL) {
754                 /* the requested block is not on the media, return all 0x00 */
755                 memset(buffer, 0, 512);
756         } else {
757                 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
758                 size_t retlen;
759                 int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
760
761                 if (res < 0 && !mtd_is_bitflip(res))
762                         return -EIO;
763         }
764         return 0;
765 }
766
767 static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
768 {
769         struct NFTLrecord *nftl = (void *)dev;
770
771         geo->heads = nftl->heads;
772         geo->sectors = nftl->sectors;
773         geo->cylinders = nftl->cylinders;
774
775         return 0;
776 }
777
778 /****************************************************************************
779  *
780  * Module stuff
781  *
782  ****************************************************************************/
783
784
785 static struct mtd_blktrans_ops nftl_tr = {
786         .name           = "nftl",
787         .major          = NFTL_MAJOR,
788         .part_bits      = NFTL_PARTN_BITS,
789         .blksize        = 512,
790         .getgeo         = nftl_getgeo,
791         .readsect       = nftl_readblock,
792 #ifdef CONFIG_NFTL_RW
793         .writesect      = nftl_writeblock,
794 #endif
795         .add_mtd        = nftl_add_mtd,
796         .remove_dev     = nftl_remove_dev,
797         .owner          = THIS_MODULE,
798 };
799
800 module_mtd_blktrans(nftl_tr);
801
802 MODULE_LICENSE("GPL");
803 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
804 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
805 MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);