Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[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                         continue;
623                 }
624
625                 /* We've found a free block. Insert it into the chain. */
626
627                 if (lastEUN != BLOCK_NIL) {
628                         thisVUC |= 0x8000; /* It's a replacement block */
629                 } else {
630                         /* The first block in a new chain */
631                         nftl->EUNtable[thisVUC] = writeEUN;
632                 }
633
634                 /* set up the actual EUN we're writing into */
635                 /* Both in our cache... */
636                 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
637
638                 /* ... and on the flash itself */
639                 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
640                               &retlen, (char *)&oob.u);
641
642                 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
643
644                 nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
645                                &retlen, (char *)&oob.u);
646
647                 /* we link the new block to the chain only after the
648                    block is ready. It avoids the case where the chain
649                    could point to a free block */
650                 if (lastEUN != BLOCK_NIL) {
651                         /* Both in our cache... */
652                         nftl->ReplUnitTable[lastEUN] = writeEUN;
653                         /* ... and on the flash itself */
654                         nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
655                                       8, &retlen, (char *)&oob.u);
656
657                         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
658                                 = cpu_to_le16(writeEUN);
659
660                         nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
661                                        8, &retlen, (char *)&oob.u);
662                 }
663
664                 return writeEUN;
665
666         } while (silly2--);
667
668         printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
669                thisVUC);
670         return BLOCK_NIL;
671 }
672
673 static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
674                            char *buffer)
675 {
676         struct NFTLrecord *nftl = (void *)mbd;
677         u16 writeEUN;
678         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
679         size_t retlen;
680         struct nftl_oob oob;
681
682         writeEUN = NFTL_findwriteunit(nftl, block);
683
684         if (writeEUN == BLOCK_NIL) {
685                 printk(KERN_WARNING
686                        "NFTL_writeblock(): Cannot find block to write to\n");
687                 /* If we _still_ haven't got a block to use, we're screwed */
688                 return 1;
689         }
690
691         memset(&oob, 0xff, sizeof(struct nftl_oob));
692         oob.b.Status = oob.b.Status1 = SECTOR_USED;
693
694         nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
695                    512, &retlen, (char *)buffer, (char *)&oob);
696         return 0;
697 }
698 #endif /* CONFIG_NFTL_RW */
699
700 static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
701                           char *buffer)
702 {
703         struct NFTLrecord *nftl = (void *)mbd;
704         struct mtd_info *mtd = nftl->mbd.mtd;
705         u16 lastgoodEUN;
706         u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
707         unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
708         unsigned int status;
709         int silly = MAX_LOOPS;
710         size_t retlen;
711         struct nftl_bci bci;
712
713         lastgoodEUN = BLOCK_NIL;
714
715         if (thisEUN != BLOCK_NIL) {
716                 while (thisEUN < nftl->nb_blocks) {
717                         if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
718                                           blockofs, 8, &retlen,
719                                           (char *)&bci) < 0)
720                                 status = SECTOR_IGNORE;
721                         else
722                                 status = bci.Status | bci.Status1;
723
724                         switch (status) {
725                         case SECTOR_FREE:
726                                 /* no modification of a sector should follow a free sector */
727                                 goto the_end;
728                         case SECTOR_DELETED:
729                                 lastgoodEUN = BLOCK_NIL;
730                                 break;
731                         case SECTOR_USED:
732                                 lastgoodEUN = thisEUN;
733                                 break;
734                         case SECTOR_IGNORE:
735                                 break;
736                         default:
737                                 printk("Unknown status for block %ld in EUN %d: %x\n",
738                                        block, thisEUN, status);
739                                 break;
740                         }
741
742                         if (!silly--) {
743                                 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
744                                        block / (nftl->EraseSize / 512));
745                                 return 1;
746                         }
747                         thisEUN = nftl->ReplUnitTable[thisEUN];
748                 }
749         }
750
751  the_end:
752         if (lastgoodEUN == BLOCK_NIL) {
753                 /* the requested block is not on the media, return all 0x00 */
754                 memset(buffer, 0, 512);
755         } else {
756                 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
757                 size_t retlen;
758                 int res = mtd_read(mtd, ptr, 512, &retlen, buffer);
759
760                 if (res < 0 && !mtd_is_bitflip(res))
761                         return -EIO;
762         }
763         return 0;
764 }
765
766 static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
767 {
768         struct NFTLrecord *nftl = (void *)dev;
769
770         geo->heads = nftl->heads;
771         geo->sectors = nftl->sectors;
772         geo->cylinders = nftl->cylinders;
773
774         return 0;
775 }
776
777 /****************************************************************************
778  *
779  * Module stuff
780  *
781  ****************************************************************************/
782
783
784 static struct mtd_blktrans_ops nftl_tr = {
785         .name           = "nftl",
786         .major          = NFTL_MAJOR,
787         .part_bits      = NFTL_PARTN_BITS,
788         .blksize        = 512,
789         .getgeo         = nftl_getgeo,
790         .readsect       = nftl_readblock,
791 #ifdef CONFIG_NFTL_RW
792         .writesect      = nftl_writeblock,
793 #endif
794         .add_mtd        = nftl_add_mtd,
795         .remove_dev     = nftl_remove_dev,
796         .owner          = THIS_MODULE,
797 };
798
799 module_mtd_blktrans(nftl_tr);
800
801 MODULE_LICENSE("GPL");
802 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
803 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
804 MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);