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