blob: dd5cea8b4a7a2dc31b1429ce6a7ebe07c143ec7b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* Linux driver for NAND Flash Translation Layer */
2/* (c) 1999 Machine Vision Holdings, Inc. */
3/* Author: David Woodhouse <dwmw2@infradead.org> */
Thomas Gleixner97894cd2005-11-07 11:15:26 +00004/* $Id: nftlcore.c,v 1.98 2005/11/07 11:14:21 gleixner Exp $ */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005
6/*
7 The contents of this file are distributed under the GNU General
8 Public License version 2. The author places no additional
9 restrictions of any kind on it.
10 */
11
12#define PRERELEASE
13
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/kernel.h>
15#include <linux/module.h>
16#include <asm/errno.h>
17#include <asm/io.h>
18#include <asm/uaccess.h>
19#include <linux/miscdevice.h>
20#include <linux/pci.h>
21#include <linux/delay.h>
22#include <linux/slab.h>
23#include <linux/sched.h>
24#include <linux/init.h>
25#include <linux/hdreg.h>
26
27#include <linux/kmod.h>
28#include <linux/mtd/mtd.h>
29#include <linux/mtd/nand.h>
30#include <linux/mtd/nftl.h>
31#include <linux/mtd/blktrans.h>
32
33/* maximum number of loops while examining next block, to have a
34 chance to detect consistency problems (they should never happen
35 because of the checks done in the mounting */
36
37#define MAX_LOOPS 10000
38
39
40static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
41{
42 struct NFTLrecord *nftl;
43 unsigned long temp;
44
45 if (mtd->type != MTD_NANDFLASH)
46 return;
47 /* OK, this is moderately ugly. But probably safe. Alternatives? */
48 if (memcmp(mtd->name, "DiskOnChip", 10))
49 return;
50
51 if (!mtd->block_isbad) {
52 printk(KERN_ERR
53"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
54"Please use the new diskonchip driver under the NAND subsystem.\n");
55 return;
56 }
57
58 DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
59
60 nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
61
62 if (!nftl) {
63 printk(KERN_WARNING "NFTL: out of memory for data structures\n");
64 return;
65 }
66 memset(nftl, 0, sizeof(*nftl));
67
68 nftl->mbd.mtd = mtd;
69 nftl->mbd.devnum = -1;
70 nftl->mbd.blksize = 512;
71 nftl->mbd.tr = tr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
73 if (NFTL_mount(nftl) < 0) {
74 printk(KERN_WARNING "NFTL: could not mount device\n");
75 kfree(nftl);
76 return;
77 }
78
79 /* OK, it's a new one. Set up all the data structures. */
80
81 /* Calculate geometry */
82 nftl->cylinders = 1024;
83 nftl->heads = 16;
84
85 temp = nftl->cylinders * nftl->heads;
86 nftl->sectors = nftl->mbd.size / temp;
87 if (nftl->mbd.size % temp) {
88 nftl->sectors++;
89 temp = nftl->cylinders * nftl->sectors;
90 nftl->heads = nftl->mbd.size / temp;
91
92 if (nftl->mbd.size % temp) {
93 nftl->heads++;
94 temp = nftl->heads * nftl->sectors;
95 nftl->cylinders = nftl->mbd.size / temp;
96 }
97 }
98
99 if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
100 /*
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000101 Oh no we don't have
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 mbd.size == heads * cylinders * sectors
103 */
104 printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
105 "match size of 0x%lx.\n", nftl->mbd.size);
106 printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
107 "(== 0x%lx sects)\n",
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000108 nftl->cylinders, nftl->heads , nftl->sectors,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 (long)nftl->cylinders * (long)nftl->heads *
110 (long)nftl->sectors );
111 }
112
113 if (add_mtd_blktrans_dev(&nftl->mbd)) {
Jesper Juhlfa671642005-11-07 01:01:27 -0800114 kfree(nftl->ReplUnitTable);
115 kfree(nftl->EUNtable);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 kfree(nftl);
117 return;
118 }
119#ifdef PSYCHO_DEBUG
120 printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
121#endif
122}
123
124static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
125{
126 struct NFTLrecord *nftl = (void *)dev;
127
128 DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
129
130 del_mtd_blktrans_dev(dev);
Jesper Juhlfa671642005-11-07 01:01:27 -0800131 kfree(nftl->ReplUnitTable);
132 kfree(nftl->EUNtable);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 kfree(nftl);
134}
135
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200136/*
137 * Read oob data from flash
138 */
139int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
140 size_t *retlen, uint8_t *buf)
141{
142 struct mtd_oob_ops ops;
143 int res;
144
145 ops.mode = MTD_OOB_PLACE;
146 ops.ooboffs = offs & (mtd->writesize - 1);
147 ops.ooblen = len;
148 ops.oobbuf = buf;
149 ops.datbuf = NULL;
150 ops.len = len;
151
152 res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
153 *retlen = ops.retlen;
154 return res;
155}
156
157/*
158 * Write oob data to flash
159 */
160int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
161 size_t *retlen, uint8_t *buf)
162{
163 struct mtd_oob_ops ops;
164 int res;
165
166 ops.mode = MTD_OOB_PLACE;
167 ops.ooboffs = offs & (mtd->writesize - 1);
168 ops.ooblen = len;
169 ops.oobbuf = buf;
170 ops.datbuf = NULL;
171 ops.len = len;
172
173 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
174 *retlen = ops.retlen;
175 return res;
176}
177
178/*
179 * Write data and oob to flash
180 */
181static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
182 size_t *retlen, uint8_t *buf, uint8_t *oob)
183{
184 struct mtd_oob_ops ops;
185 int res;
186
187 ops.mode = MTD_OOB_PLACE;
188 ops.ooboffs = offs;
189 ops.ooblen = mtd->oobsize;
190 ops.oobbuf = oob;
191 ops.datbuf = buf;
192 ops.len = len;
193
194 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
195 *retlen = ops.retlen;
196 return res;
197}
198
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199#ifdef CONFIG_NFTL_RW
200
201/* Actual NFTL access routines */
202/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
203 * when the give Virtual Unit Chain
204 */
205static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
206{
207 /* For a given Virtual Unit Chain: find or create a free block and
208 add it to the chain */
209 /* We're passed the number of the last EUN in the chain, to save us from
210 having to look it up again */
211 u16 pot = nftl->LastFreeEUN;
212 int silly = nftl->nb_blocks;
213
214 /* Normally, we force a fold to happen before we run out of free blocks completely */
215 if (!desperate && nftl->numfreeEUNs < 2) {
216 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
217 return 0xffff;
218 }
219
220 /* Scan for a free block */
221 do {
222 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
223 nftl->LastFreeEUN = pot;
224 nftl->numfreeEUNs--;
225 return pot;
226 }
227
228 /* This will probably point to the MediaHdr unit itself,
229 right at the beginning of the partition. But that unit
230 (and the backup unit too) should have the UCI set
231 up so that it's not selected for overwriting */
232 if (++pot > nftl->lastEUN)
233 pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
234
235 if (!silly--) {
236 printk("Argh! No free blocks found! LastFreeEUN = %d, "
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000237 "FirstEUN = %d\n", nftl->LastFreeEUN,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
239 return 0xffff;
240 }
241 } while (pot != nftl->LastFreeEUN);
242
243 return 0xffff;
244}
245
246static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
247{
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200248 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 u16 BlockMap[MAX_SECTORS_PER_UNIT];
250 unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
251 unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
252 unsigned int thisEUN;
253 int block;
254 int silly;
255 unsigned int targetEUN;
256 struct nftl_oob oob;
257 int inplace = 1;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200258 size_t retlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
260 memset(BlockMap, 0xff, sizeof(BlockMap));
261 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
262
263 thisEUN = nftl->EUNtable[thisVUC];
264
265 if (thisEUN == BLOCK_NIL) {
266 printk(KERN_WARNING "Trying to fold non-existent "
267 "Virtual Unit Chain %d!\n", thisVUC);
268 return BLOCK_NIL;
269 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000270
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 /* Scan to find the Erase Unit which holds the actual data for each
272 512-byte block within the Chain.
273 */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200274 silly = MAX_LOOPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 targetEUN = BLOCK_NIL;
276 while (thisEUN <= nftl->lastEUN ) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200277 unsigned int status, foldmark;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
279 targetEUN = thisEUN;
280 for (block = 0; block < nftl->EraseSize / 512; block ++) {
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200281 nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200282 (block * 512), 16 , &retlen,
283 (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 if (block == 2) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200285 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
286 if (foldmark == FOLD_MARK_IN_PROGRESS) {
287 DEBUG(MTD_DEBUG_LEVEL1,
288 "Write Inhibited on EUN %d\n", thisEUN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 inplace = 0;
290 } else {
291 /* There's no other reason not to do inplace,
292 except ones that come later. So we don't need
293 to preserve inplace */
294 inplace = 1;
295 }
296 }
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200297 status = oob.b.Status | oob.b.Status1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 BlockLastState[block] = status;
299
300 switch(status) {
301 case SECTOR_FREE:
302 BlockFreeFound[block] = 1;
303 break;
304
305 case SECTOR_USED:
306 if (!BlockFreeFound[block])
307 BlockMap[block] = thisEUN;
308 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000309 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 "SECTOR_USED found after SECTOR_FREE "
311 "in Virtual Unit Chain %d for block %d\n",
312 thisVUC, block);
313 break;
314 case SECTOR_DELETED:
315 if (!BlockFreeFound[block])
316 BlockMap[block] = BLOCK_NIL;
317 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000318 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 "SECTOR_DELETED found after SECTOR_FREE "
320 "in Virtual Unit Chain %d for block %d\n",
321 thisVUC, block);
322 break;
323
324 case SECTOR_IGNORE:
325 break;
326 default:
327 printk("Unknown status for block %d in EUN %d: %x\n",
328 block, thisEUN, status);
329 }
330 }
331
332 if (!silly--) {
333 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
334 thisVUC);
335 return BLOCK_NIL;
336 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000337
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 thisEUN = nftl->ReplUnitTable[thisEUN];
339 }
340
341 if (inplace) {
342 /* We're being asked to be a fold-in-place. Check
343 that all blocks which actually have data associated
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000344 with them (i.e. BlockMap[block] != BLOCK_NIL) are
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 either already present or SECTOR_FREE in the target
346 block. If not, we're going to have to fold out-of-place
347 anyway.
348 */
349 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
350 if (BlockLastState[block] != SECTOR_FREE &&
351 BlockMap[block] != BLOCK_NIL &&
352 BlockMap[block] != targetEUN) {
353 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
354 "block %d was %x lastEUN, "
355 "and is in EUN %d (%s) %d\n",
356 thisVUC, block, BlockLastState[block],
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000357 BlockMap[block],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 BlockMap[block]== targetEUN ? "==" : "!=",
359 targetEUN);
360 inplace = 0;
361 break;
362 }
363 }
364
365 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
366 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
367 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
368 SECTOR_FREE) {
369 DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
370 "Folding out of place.\n", targetEUN);
371 inplace = 0;
372 }
373 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000374
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 if (!inplace) {
376 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
377 "Trying out-of-place\n", thisVUC);
378 /* We need to find a targetEUN to fold into. */
379 targetEUN = NFTL_findfreeblock(nftl, 1);
380 if (targetEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000381 /* Ouch. Now we're screwed. We need to do a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 fold-in-place of another chain to make room
383 for this one. We need a better way of selecting
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000384 which chain to fold, because makefreeblock will
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 only ask us to fold the same one again.
386 */
387 printk(KERN_WARNING
388 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
389 return BLOCK_NIL;
390 }
391 } else {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200392 /* We put a fold mark in the chain we are folding only if we
393 fold in place to help the mount check code. If we do not fold in
394 place, it is possible to find the valid chain by selecting the
395 longer one */
396 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
397 oob.u.c.unused = 0xffffffff;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200398 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200399 8, &retlen, (char *)&oob.u);
400 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
402 /* OK. We now know the location of every block in the Virtual Unit Chain,
403 and the Erase Unit into which we are supposed to be copying.
404 Go for it.
405 */
406 DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
407 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
408 unsigned char movebuf[512];
409 int ret;
410
411 /* If it's in the target EUN already, or if it's pending write, do nothing */
412 if (BlockMap[block] == targetEUN ||
413 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
414 continue;
415 }
416
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200417 /* copy only in non free block (free blocks can only
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 happen in case of media errors or deleted blocks) */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200419 if (BlockMap[block] == BLOCK_NIL)
420 continue;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000421
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200422 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
423 512, &retlen, movebuf);
Thomas Gleixner9a1fcdf2006-05-29 14:56:39 +0200424 if (ret < 0 && ret != -EUCLEAN) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200425 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
426 + (block * 512), 512, &retlen,
427 movebuf);
428 if (ret != -EIO)
429 printk("Error went away on retry.\n");
430 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 memset(&oob, 0xff, sizeof(struct nftl_oob));
432 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Thomas Gleixner9223a452006-05-23 17:21:03 +0200433
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200434 nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
435 (block * 512), 512, &retlen, movebuf, (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000437
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200438 /* add the header so that it is now a valid chain */
439 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
440 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000441
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200442 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200443 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444
445 /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
446
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000447 /* At this point, we have two different chains for this Virtual Unit, and no way to tell
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 them apart. If we crash now, we get confused. However, both contain the same data, so we
449 shouldn't actually lose data in this case. It's just that when we load up on a medium which
450 has duplicate chains, we need to free one of the chains because it's not necessary any more.
451 */
452 thisEUN = nftl->EUNtable[thisVUC];
453 DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
454
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000455 /* For each block in the old chain (except the targetEUN of course),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 free it and make it available for future use */
457 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
458 unsigned int EUNtmp;
459
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200460 EUNtmp = nftl->ReplUnitTable[thisEUN];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200462 if (NFTL_formatblock(nftl, thisEUN) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 /* could not erase : mark block as reserved
464 */
465 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200466 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 /* correctly erased : mark it as free */
468 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
469 nftl->numfreeEUNs++;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200470 }
471 thisEUN = EUNtmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000473
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 /* Make this the new start of chain for thisVUC */
475 nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
476 nftl->EUNtable[thisVUC] = targetEUN;
477
478 return targetEUN;
479}
480
481static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
482{
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000483 /* This is the part that needs some cleverness applied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 For now, I'm doing the minimum applicable to actually
485 get the thing to work.
486 Wear-levelling and other clever stuff needs to be implemented
487 and we also need to do some assessment of the results when
488 the system loses power half-way through the routine.
489 */
490 u16 LongestChain = 0;
491 u16 ChainLength = 0, thislen;
492 u16 chain, EUN;
493
494 for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
495 EUN = nftl->EUNtable[chain];
496 thislen = 0;
497
498 while (EUN <= nftl->lastEUN) {
499 thislen++;
500 //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
501 EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
502 if (thislen > 0xff00) {
503 printk("Endless loop in Virtual Chain %d: Unit %x\n",
504 chain, EUN);
505 }
506 if (thislen > 0xff10) {
507 /* Actually, don't return failure. Just ignore this chain and
508 get on with it. */
509 thislen = 0;
510 break;
511 }
512 }
513
514 if (thislen > ChainLength) {
515 //printk("New longest chain is %d with length %d\n", chain, thislen);
516 ChainLength = thislen;
517 LongestChain = chain;
518 }
519 }
520
521 if (ChainLength < 2) {
522 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
523 "Failing request\n");
524 return 0xffff;
525 }
526
527 return NFTL_foldchain (nftl, LongestChain, pendingblock);
528}
529
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000530/* NFTL_findwriteunit: Return the unit number into which we can write
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 for this block. Make it available if it isn't already
532*/
533static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
534{
535 u16 lastEUN;
536 u16 thisVUC = block / (nftl->EraseSize / 512);
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200537 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 unsigned int writeEUN;
539 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
540 size_t retlen;
541 int silly, silly2 = 3;
542 struct nftl_oob oob;
543
544 do {
545 /* Scan the media to find a unit in the VUC which has
546 a free space for the block in question.
547 */
548
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000549 /* This condition catches the 0x[7f]fff cases, as well as
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 being a sanity check for past-end-of-media access
551 */
552 lastEUN = BLOCK_NIL;
553 writeEUN = nftl->EUNtable[thisVUC];
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200554 silly = MAX_LOOPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 while (writeEUN <= nftl->lastEUN) {
556 struct nftl_bci bci;
557 size_t retlen;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200558 unsigned int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
560 lastEUN = writeEUN;
561
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200562 nftl_read_oob(mtd,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200563 (writeEUN * nftl->EraseSize) + blockofs,
564 8, &retlen, (char *)&bci);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000565
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
567 block , writeEUN, le16_to_cpu(bci.Status));
568
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200569 status = bci.Status | bci.Status1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 switch(status) {
571 case SECTOR_FREE:
572 return writeEUN;
573
574 case SECTOR_DELETED:
575 case SECTOR_USED:
576 case SECTOR_IGNORE:
577 break;
578 default:
579 // Invalid block. Don't use it any more. Must implement.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000580 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000582
583 if (!silly--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 printk(KERN_WARNING
585 "Infinite loop in Virtual Unit Chain 0x%x\n",
586 thisVUC);
587 return 0xffff;
588 }
589
590 /* Skip to next block in chain */
591 writeEUN = nftl->ReplUnitTable[writeEUN];
592 }
593
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000594 /* OK. We didn't find one in the existing chain, or there
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 is no existing chain. */
596
597 /* Try to find an already-free block */
598 writeEUN = NFTL_findfreeblock(nftl, 0);
599
600 if (writeEUN == BLOCK_NIL) {
601 /* That didn't work - there were no free blocks just
602 waiting to be picked up. We're going to have to fold
603 a chain to make room.
604 */
605
606 /* First remember the start of this chain */
607 //u16 startEUN = nftl->EUNtable[thisVUC];
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
610 writeEUN = NFTL_makefreeblock(nftl, 0xffff);
611
612 if (writeEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000613 /* OK, we accept that the above comment is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 lying - there may have been free blocks
615 last time we called NFTL_findfreeblock(),
616 but they are reserved for when we're
617 desperate. Well, now we're desperate.
618 */
619 DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
620 writeEUN = NFTL_findfreeblock(nftl, 1);
621 }
622 if (writeEUN == BLOCK_NIL) {
623 /* Ouch. This should never happen - we should
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000624 always be able to make some room somehow.
625 If we get here, we've allocated more storage
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 space than actual media, or our makefreeblock
627 routine is missing something.
628 */
629 printk(KERN_WARNING "Cannot make free space.\n");
630 return BLOCK_NIL;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000631 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 //printk("Restarting scan\n");
633 lastEUN = BLOCK_NIL;
634 continue;
635 }
636
637 /* We've found a free block. Insert it into the chain. */
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000638
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 if (lastEUN != BLOCK_NIL) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200640 thisVUC |= 0x8000; /* It's a replacement block */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 } else {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200642 /* The first block in a new chain */
643 nftl->EUNtable[thisVUC] = writeEUN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 }
645
646 /* set up the actual EUN we're writing into */
647 /* Both in our cache... */
648 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
649
650 /* ... and on the flash itself */
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200651 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200652 &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
654 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
655
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200656 nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200657 &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200659 /* we link the new block to the chain only after the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 block is ready. It avoids the case where the chain
661 could point to a free block */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200662 if (lastEUN != BLOCK_NIL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 /* Both in our cache... */
664 nftl->ReplUnitTable[lastEUN] = writeEUN;
665 /* ... and on the flash itself */
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200666 nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200667 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
669 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
670 = cpu_to_le16(writeEUN);
671
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200672 nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200673 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 }
675
676 return writeEUN;
677
678 } while (silly2--);
679
680 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
681 thisVUC);
682 return 0xffff;
683}
684
685static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
686 char *buffer)
687{
688 struct NFTLrecord *nftl = (void *)mbd;
689 u16 writeEUN;
690 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
691 size_t retlen;
692 struct nftl_oob oob;
693
694 writeEUN = NFTL_findwriteunit(nftl, block);
695
696 if (writeEUN == BLOCK_NIL) {
697 printk(KERN_WARNING
698 "NFTL_writeblock(): Cannot find block to write to\n");
699 /* If we _still_ haven't got a block to use, we're screwed */
700 return 1;
701 }
702
703 memset(&oob, 0xff, sizeof(struct nftl_oob));
704 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200706 nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
707 512, &retlen, (char *)buffer, (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 return 0;
709}
710#endif /* CONFIG_NFTL_RW */
711
712static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
713 char *buffer)
714{
715 struct NFTLrecord *nftl = (void *)mbd;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200716 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 u16 lastgoodEUN;
718 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
719 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200720 unsigned int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 int silly = MAX_LOOPS;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200722 size_t retlen;
723 struct nftl_bci bci;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
725 lastgoodEUN = BLOCK_NIL;
726
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200727 if (thisEUN != BLOCK_NIL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 while (thisEUN < nftl->nb_blocks) {
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200729 if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200730 blockofs, 8, &retlen,
731 (char *)&bci) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 status = SECTOR_IGNORE;
733 else
734 status = bci.Status | bci.Status1;
735
736 switch (status) {
737 case SECTOR_FREE:
738 /* no modification of a sector should follow a free sector */
739 goto the_end;
740 case SECTOR_DELETED:
741 lastgoodEUN = BLOCK_NIL;
742 break;
743 case SECTOR_USED:
744 lastgoodEUN = thisEUN;
745 break;
746 case SECTOR_IGNORE:
747 break;
748 default:
749 printk("Unknown status for block %ld in EUN %d: %x\n",
750 block, thisEUN, status);
751 break;
752 }
753
754 if (!silly--) {
755 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
756 block / (nftl->EraseSize / 512));
757 return 1;
758 }
759 thisEUN = nftl->ReplUnitTable[thisEUN];
760 }
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200761 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
763 the_end:
764 if (lastgoodEUN == BLOCK_NIL) {
765 /* the requested block is not on the media, return all 0x00 */
766 memset(buffer, 0, 512);
767 } else {
768 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
769 size_t retlen;
Thomas Gleixner9a1fcdf2006-05-29 14:56:39 +0200770 int res = mtd->read(mtd, ptr, 512, &retlen, buffer);
771
772 if (res < 0 && res != -EUCLEAN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 return -EIO;
774 }
775 return 0;
776}
777
778static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
779{
780 struct NFTLrecord *nftl = (void *)dev;
781
782 geo->heads = nftl->heads;
783 geo->sectors = nftl->sectors;
784 geo->cylinders = nftl->cylinders;
785
786 return 0;
787}
788
789/****************************************************************************
790 *
791 * Module stuff
792 *
793 ****************************************************************************/
794
795
796static struct mtd_blktrans_ops nftl_tr = {
797 .name = "nftl",
798 .major = NFTL_MAJOR,
799 .part_bits = NFTL_PARTN_BITS,
800 .getgeo = nftl_getgeo,
801 .readsect = nftl_readblock,
802#ifdef CONFIG_NFTL_RW
803 .writesect = nftl_writeblock,
804#endif
805 .add_mtd = nftl_add_mtd,
806 .remove_dev = nftl_remove_dev,
807 .owner = THIS_MODULE,
808};
809
810extern char nftlmountrev[];
811
812static int __init init_nftl(void)
813{
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000814 printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
816 return register_mtd_blktrans(&nftl_tr);
817}
818
819static void __exit cleanup_nftl(void)
820{
821 deregister_mtd_blktrans(&nftl_tr);
822}
823
824module_init(init_nftl);
825module_exit(cleanup_nftl);
826
827MODULE_LICENSE("GPL");
828MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
829MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");