blob: 0c9ce19ea27a3476f44384749476822b3c311cfa [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/delay.h>
21#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/hdreg.h>
24
25#include <linux/kmod.h>
26#include <linux/mtd/mtd.h>
27#include <linux/mtd/nand.h>
28#include <linux/mtd/nftl.h>
29#include <linux/mtd/blktrans.h>
30
31/* maximum number of loops while examining next block, to have a
32 chance to detect consistency problems (they should never happen
33 because of the checks done in the mounting */
34
35#define MAX_LOOPS 10000
36
37
38static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
39{
40 struct NFTLrecord *nftl;
41 unsigned long temp;
42
43 if (mtd->type != MTD_NANDFLASH)
44 return;
45 /* OK, this is moderately ugly. But probably safe. Alternatives? */
46 if (memcmp(mtd->name, "DiskOnChip", 10))
47 return;
48
49 if (!mtd->block_isbad) {
50 printk(KERN_ERR
51"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
52"Please use the new diskonchip driver under the NAND subsystem.\n");
53 return;
54 }
55
56 DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
57
Burman Yan95b93a02006-11-15 21:10:29 +020058 nftl = kzalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60 if (!nftl) {
61 printk(KERN_WARNING "NFTL: out of memory for data structures\n");
62 return;
63 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
65 nftl->mbd.mtd = mtd;
66 nftl->mbd.devnum = -1;
Richard Purdie19187672006-10-27 09:09:33 +010067
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 nftl->mbd.tr = tr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
70 if (NFTL_mount(nftl) < 0) {
71 printk(KERN_WARNING "NFTL: could not mount device\n");
72 kfree(nftl);
73 return;
74 }
75
76 /* OK, it's a new one. Set up all the data structures. */
77
78 /* Calculate geometry */
79 nftl->cylinders = 1024;
80 nftl->heads = 16;
81
82 temp = nftl->cylinders * nftl->heads;
83 nftl->sectors = nftl->mbd.size / temp;
84 if (nftl->mbd.size % temp) {
85 nftl->sectors++;
86 temp = nftl->cylinders * nftl->sectors;
87 nftl->heads = nftl->mbd.size / temp;
88
89 if (nftl->mbd.size % temp) {
90 nftl->heads++;
91 temp = nftl->heads * nftl->sectors;
92 nftl->cylinders = nftl->mbd.size / temp;
93 }
94 }
95
96 if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
97 /*
Thomas Gleixner97894cd2005-11-07 11:15:26 +000098 Oh no we don't have
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 mbd.size == heads * cylinders * sectors
100 */
101 printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
102 "match size of 0x%lx.\n", nftl->mbd.size);
103 printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
104 "(== 0x%lx sects)\n",
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000105 nftl->cylinders, nftl->heads , nftl->sectors,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 (long)nftl->cylinders * (long)nftl->heads *
107 (long)nftl->sectors );
108 }
109
110 if (add_mtd_blktrans_dev(&nftl->mbd)) {
Jesper Juhlfa671642005-11-07 01:01:27 -0800111 kfree(nftl->ReplUnitTable);
112 kfree(nftl->EUNtable);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 kfree(nftl);
114 return;
115 }
116#ifdef PSYCHO_DEBUG
117 printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
118#endif
119}
120
121static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
122{
123 struct NFTLrecord *nftl = (void *)dev;
124
125 DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
126
127 del_mtd_blktrans_dev(dev);
Jesper Juhlfa671642005-11-07 01:01:27 -0800128 kfree(nftl->ReplUnitTable);
129 kfree(nftl->EUNtable);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 kfree(nftl);
131}
132
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200133/*
134 * Read oob data from flash
135 */
136int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
137 size_t *retlen, uint8_t *buf)
138{
139 struct mtd_oob_ops ops;
140 int res;
141
142 ops.mode = MTD_OOB_PLACE;
143 ops.ooboffs = offs & (mtd->writesize - 1);
144 ops.ooblen = len;
145 ops.oobbuf = buf;
146 ops.datbuf = NULL;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200147
148 res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
Vitaly Wool70145682006-11-03 18:20:38 +0300149 *retlen = ops.oobretlen;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200150 return res;
151}
152
153/*
154 * Write oob data to flash
155 */
156int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
157 size_t *retlen, uint8_t *buf)
158{
159 struct mtd_oob_ops ops;
160 int res;
161
162 ops.mode = MTD_OOB_PLACE;
163 ops.ooboffs = offs & (mtd->writesize - 1);
164 ops.ooblen = len;
165 ops.oobbuf = buf;
166 ops.datbuf = NULL;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200167
168 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
Vitaly Wool70145682006-11-03 18:20:38 +0300169 *retlen = ops.oobretlen;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200170 return res;
171}
172
Frederik Deweerdt553a8012006-10-02 09:42:25 +0100173#ifdef CONFIG_NFTL_RW
174
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200175/*
176 * Write data and oob to flash
177 */
178static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
179 size_t *retlen, uint8_t *buf, uint8_t *oob)
180{
181 struct mtd_oob_ops ops;
182 int res;
183
184 ops.mode = MTD_OOB_PLACE;
185 ops.ooboffs = offs;
186 ops.ooblen = mtd->oobsize;
187 ops.oobbuf = oob;
188 ops.datbuf = buf;
189 ops.len = len;
190
191 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
192 *retlen = ops.retlen;
193 return res;
194}
195
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196/* Actual NFTL access routines */
197/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
198 * when the give Virtual Unit Chain
199 */
200static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
201{
202 /* For a given Virtual Unit Chain: find or create a free block and
203 add it to the chain */
204 /* We're passed the number of the last EUN in the chain, to save us from
205 having to look it up again */
206 u16 pot = nftl->LastFreeEUN;
207 int silly = nftl->nb_blocks;
208
209 /* Normally, we force a fold to happen before we run out of free blocks completely */
210 if (!desperate && nftl->numfreeEUNs < 2) {
211 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
212 return 0xffff;
213 }
214
215 /* Scan for a free block */
216 do {
217 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
218 nftl->LastFreeEUN = pot;
219 nftl->numfreeEUNs--;
220 return pot;
221 }
222
223 /* This will probably point to the MediaHdr unit itself,
224 right at the beginning of the partition. But that unit
225 (and the backup unit too) should have the UCI set
226 up so that it's not selected for overwriting */
227 if (++pot > nftl->lastEUN)
228 pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
229
230 if (!silly--) {
231 printk("Argh! No free blocks found! LastFreeEUN = %d, "
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000232 "FirstEUN = %d\n", nftl->LastFreeEUN,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
234 return 0xffff;
235 }
236 } while (pot != nftl->LastFreeEUN);
237
238 return 0xffff;
239}
240
241static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
242{
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200243 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 u16 BlockMap[MAX_SECTORS_PER_UNIT];
245 unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
246 unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
247 unsigned int thisEUN;
248 int block;
249 int silly;
250 unsigned int targetEUN;
251 struct nftl_oob oob;
252 int inplace = 1;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200253 size_t retlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
255 memset(BlockMap, 0xff, sizeof(BlockMap));
256 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
257
258 thisEUN = nftl->EUNtable[thisVUC];
259
260 if (thisEUN == BLOCK_NIL) {
261 printk(KERN_WARNING "Trying to fold non-existent "
262 "Virtual Unit Chain %d!\n", thisVUC);
263 return BLOCK_NIL;
264 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000265
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 /* Scan to find the Erase Unit which holds the actual data for each
267 512-byte block within the Chain.
268 */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200269 silly = MAX_LOOPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 targetEUN = BLOCK_NIL;
271 while (thisEUN <= nftl->lastEUN ) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200272 unsigned int status, foldmark;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273
274 targetEUN = thisEUN;
275 for (block = 0; block < nftl->EraseSize / 512; block ++) {
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200276 nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200277 (block * 512), 16 , &retlen,
278 (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 if (block == 2) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200280 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
281 if (foldmark == FOLD_MARK_IN_PROGRESS) {
282 DEBUG(MTD_DEBUG_LEVEL1,
283 "Write Inhibited on EUN %d\n", thisEUN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 inplace = 0;
285 } else {
286 /* There's no other reason not to do inplace,
287 except ones that come later. So we don't need
288 to preserve inplace */
289 inplace = 1;
290 }
291 }
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200292 status = oob.b.Status | oob.b.Status1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 BlockLastState[block] = status;
294
295 switch(status) {
296 case SECTOR_FREE:
297 BlockFreeFound[block] = 1;
298 break;
299
300 case SECTOR_USED:
301 if (!BlockFreeFound[block])
302 BlockMap[block] = thisEUN;
303 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000304 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 "SECTOR_USED found after SECTOR_FREE "
306 "in Virtual Unit Chain %d for block %d\n",
307 thisVUC, block);
308 break;
309 case SECTOR_DELETED:
310 if (!BlockFreeFound[block])
311 BlockMap[block] = BLOCK_NIL;
312 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000313 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 "SECTOR_DELETED found after SECTOR_FREE "
315 "in Virtual Unit Chain %d for block %d\n",
316 thisVUC, block);
317 break;
318
319 case SECTOR_IGNORE:
320 break;
321 default:
322 printk("Unknown status for block %d in EUN %d: %x\n",
323 block, thisEUN, status);
324 }
325 }
326
327 if (!silly--) {
328 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
329 thisVUC);
330 return BLOCK_NIL;
331 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000332
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 thisEUN = nftl->ReplUnitTable[thisEUN];
334 }
335
336 if (inplace) {
337 /* We're being asked to be a fold-in-place. Check
338 that all blocks which actually have data associated
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000339 with them (i.e. BlockMap[block] != BLOCK_NIL) are
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 either already present or SECTOR_FREE in the target
341 block. If not, we're going to have to fold out-of-place
342 anyway.
343 */
344 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
345 if (BlockLastState[block] != SECTOR_FREE &&
346 BlockMap[block] != BLOCK_NIL &&
347 BlockMap[block] != targetEUN) {
348 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
349 "block %d was %x lastEUN, "
350 "and is in EUN %d (%s) %d\n",
351 thisVUC, block, BlockLastState[block],
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000352 BlockMap[block],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 BlockMap[block]== targetEUN ? "==" : "!=",
354 targetEUN);
355 inplace = 0;
356 break;
357 }
358 }
359
360 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
361 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
362 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
363 SECTOR_FREE) {
364 DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
365 "Folding out of place.\n", targetEUN);
366 inplace = 0;
367 }
368 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 if (!inplace) {
371 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
372 "Trying out-of-place\n", thisVUC);
373 /* We need to find a targetEUN to fold into. */
374 targetEUN = NFTL_findfreeblock(nftl, 1);
375 if (targetEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000376 /* Ouch. Now we're screwed. We need to do a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 fold-in-place of another chain to make room
378 for this one. We need a better way of selecting
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000379 which chain to fold, because makefreeblock will
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 only ask us to fold the same one again.
381 */
382 printk(KERN_WARNING
383 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
384 return BLOCK_NIL;
385 }
386 } else {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200387 /* We put a fold mark in the chain we are folding only if we
388 fold in place to help the mount check code. If we do not fold in
389 place, it is possible to find the valid chain by selecting the
390 longer one */
391 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
392 oob.u.c.unused = 0xffffffff;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200393 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200394 8, &retlen, (char *)&oob.u);
395 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396
397 /* OK. We now know the location of every block in the Virtual Unit Chain,
398 and the Erase Unit into which we are supposed to be copying.
399 Go for it.
400 */
401 DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
402 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
403 unsigned char movebuf[512];
404 int ret;
405
406 /* If it's in the target EUN already, or if it's pending write, do nothing */
407 if (BlockMap[block] == targetEUN ||
408 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
409 continue;
410 }
411
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200412 /* copy only in non free block (free blocks can only
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 happen in case of media errors or deleted blocks) */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200414 if (BlockMap[block] == BLOCK_NIL)
415 continue;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000416
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200417 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
418 512, &retlen, movebuf);
Thomas Gleixner9a1fcdf2006-05-29 14:56:39 +0200419 if (ret < 0 && ret != -EUCLEAN) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200420 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
421 + (block * 512), 512, &retlen,
422 movebuf);
423 if (ret != -EIO)
424 printk("Error went away on retry.\n");
425 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 memset(&oob, 0xff, sizeof(struct nftl_oob));
427 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Thomas Gleixner9223a452006-05-23 17:21:03 +0200428
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200429 nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
430 (block * 512), 512, &retlen, movebuf, (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000432
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200433 /* add the header so that it is now a valid chain */
434 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
435 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000436
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200437 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200438 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439
440 /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
441
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000442 /* At this point, we have two different chains for this Virtual Unit, and no way to tell
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 them apart. If we crash now, we get confused. However, both contain the same data, so we
444 shouldn't actually lose data in this case. It's just that when we load up on a medium which
445 has duplicate chains, we need to free one of the chains because it's not necessary any more.
446 */
447 thisEUN = nftl->EUNtable[thisVUC];
448 DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
449
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000450 /* For each block in the old chain (except the targetEUN of course),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 free it and make it available for future use */
452 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
453 unsigned int EUNtmp;
454
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200455 EUNtmp = nftl->ReplUnitTable[thisEUN];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200457 if (NFTL_formatblock(nftl, thisEUN) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 /* could not erase : mark block as reserved
459 */
460 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200461 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 /* correctly erased : mark it as free */
463 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
464 nftl->numfreeEUNs++;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200465 }
466 thisEUN = EUNtmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 /* Make this the new start of chain for thisVUC */
470 nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
471 nftl->EUNtable[thisVUC] = targetEUN;
472
473 return targetEUN;
474}
475
476static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
477{
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000478 /* This is the part that needs some cleverness applied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 For now, I'm doing the minimum applicable to actually
480 get the thing to work.
481 Wear-levelling and other clever stuff needs to be implemented
482 and we also need to do some assessment of the results when
483 the system loses power half-way through the routine.
484 */
485 u16 LongestChain = 0;
486 u16 ChainLength = 0, thislen;
487 u16 chain, EUN;
488
489 for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
490 EUN = nftl->EUNtable[chain];
491 thislen = 0;
492
493 while (EUN <= nftl->lastEUN) {
494 thislen++;
495 //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
496 EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
497 if (thislen > 0xff00) {
498 printk("Endless loop in Virtual Chain %d: Unit %x\n",
499 chain, EUN);
500 }
501 if (thislen > 0xff10) {
502 /* Actually, don't return failure. Just ignore this chain and
503 get on with it. */
504 thislen = 0;
505 break;
506 }
507 }
508
509 if (thislen > ChainLength) {
510 //printk("New longest chain is %d with length %d\n", chain, thislen);
511 ChainLength = thislen;
512 LongestChain = chain;
513 }
514 }
515
516 if (ChainLength < 2) {
517 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
518 "Failing request\n");
519 return 0xffff;
520 }
521
522 return NFTL_foldchain (nftl, LongestChain, pendingblock);
523}
524
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000525/* NFTL_findwriteunit: Return the unit number into which we can write
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 for this block. Make it available if it isn't already
527*/
528static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
529{
530 u16 lastEUN;
531 u16 thisVUC = block / (nftl->EraseSize / 512);
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200532 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 unsigned int writeEUN;
534 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
535 size_t retlen;
536 int silly, silly2 = 3;
537 struct nftl_oob oob;
538
539 do {
540 /* Scan the media to find a unit in the VUC which has
541 a free space for the block in question.
542 */
543
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000544 /* This condition catches the 0x[7f]fff cases, as well as
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 being a sanity check for past-end-of-media access
546 */
547 lastEUN = BLOCK_NIL;
548 writeEUN = nftl->EUNtable[thisVUC];
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200549 silly = MAX_LOOPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 while (writeEUN <= nftl->lastEUN) {
551 struct nftl_bci bci;
552 size_t retlen;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200553 unsigned int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
555 lastEUN = writeEUN;
556
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200557 nftl_read_oob(mtd,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200558 (writeEUN * nftl->EraseSize) + blockofs,
559 8, &retlen, (char *)&bci);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000560
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
562 block , writeEUN, le16_to_cpu(bci.Status));
563
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200564 status = bci.Status | bci.Status1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 switch(status) {
566 case SECTOR_FREE:
567 return writeEUN;
568
569 case SECTOR_DELETED:
570 case SECTOR_USED:
571 case SECTOR_IGNORE:
572 break;
573 default:
574 // Invalid block. Don't use it any more. Must implement.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000575 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000577
578 if (!silly--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 printk(KERN_WARNING
580 "Infinite loop in Virtual Unit Chain 0x%x\n",
581 thisVUC);
582 return 0xffff;
583 }
584
585 /* Skip to next block in chain */
586 writeEUN = nftl->ReplUnitTable[writeEUN];
587 }
588
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000589 /* OK. We didn't find one in the existing chain, or there
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 is no existing chain. */
591
592 /* Try to find an already-free block */
593 writeEUN = NFTL_findfreeblock(nftl, 0);
594
595 if (writeEUN == BLOCK_NIL) {
596 /* That didn't work - there were no free blocks just
597 waiting to be picked up. We're going to have to fold
598 a chain to make room.
599 */
600
601 /* First remember the start of this chain */
602 //u16 startEUN = nftl->EUNtable[thisVUC];
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000603
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
605 writeEUN = NFTL_makefreeblock(nftl, 0xffff);
606
607 if (writeEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000608 /* OK, we accept that the above comment is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 lying - there may have been free blocks
610 last time we called NFTL_findfreeblock(),
611 but they are reserved for when we're
612 desperate. Well, now we're desperate.
613 */
614 DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
615 writeEUN = NFTL_findfreeblock(nftl, 1);
616 }
617 if (writeEUN == BLOCK_NIL) {
618 /* Ouch. This should never happen - we should
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000619 always be able to make some room somehow.
620 If we get here, we've allocated more storage
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 space than actual media, or our makefreeblock
622 routine is missing something.
623 */
624 printk(KERN_WARNING "Cannot make free space.\n");
625 return BLOCK_NIL;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000626 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 //printk("Restarting scan\n");
628 lastEUN = BLOCK_NIL;
629 continue;
630 }
631
632 /* We've found a free block. Insert it into the chain. */
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000633
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 if (lastEUN != BLOCK_NIL) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200635 thisVUC |= 0x8000; /* It's a replacement block */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 } else {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200637 /* The first block in a new chain */
638 nftl->EUNtable[thisVUC] = writeEUN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 }
640
641 /* set up the actual EUN we're writing into */
642 /* Both in our cache... */
643 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
644
645 /* ... and on the flash itself */
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200646 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200647 &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
649 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
650
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200651 nftl_write_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
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200654 /* we link the new block to the chain only after the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 block is ready. It avoids the case where the chain
656 could point to a free block */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200657 if (lastEUN != BLOCK_NIL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 /* Both in our cache... */
659 nftl->ReplUnitTable[lastEUN] = writeEUN;
660 /* ... and on the flash itself */
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200661 nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200662 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
664 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
665 = cpu_to_le16(writeEUN);
666
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200667 nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200668 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 }
670
671 return writeEUN;
672
673 } while (silly2--);
674
675 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
676 thisVUC);
677 return 0xffff;
678}
679
680static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
681 char *buffer)
682{
683 struct NFTLrecord *nftl = (void *)mbd;
684 u16 writeEUN;
685 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
686 size_t retlen;
687 struct nftl_oob oob;
688
689 writeEUN = NFTL_findwriteunit(nftl, block);
690
691 if (writeEUN == BLOCK_NIL) {
692 printk(KERN_WARNING
693 "NFTL_writeblock(): Cannot find block to write to\n");
694 /* If we _still_ haven't got a block to use, we're screwed */
695 return 1;
696 }
697
698 memset(&oob, 0xff, sizeof(struct nftl_oob));
699 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200701 nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
702 512, &retlen, (char *)buffer, (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 return 0;
704}
705#endif /* CONFIG_NFTL_RW */
706
707static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
708 char *buffer)
709{
710 struct NFTLrecord *nftl = (void *)mbd;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200711 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 u16 lastgoodEUN;
713 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
714 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200715 unsigned int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 int silly = MAX_LOOPS;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200717 size_t retlen;
718 struct nftl_bci bci;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719
720 lastgoodEUN = BLOCK_NIL;
721
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200722 if (thisEUN != BLOCK_NIL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 while (thisEUN < nftl->nb_blocks) {
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200724 if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200725 blockofs, 8, &retlen,
726 (char *)&bci) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 status = SECTOR_IGNORE;
728 else
729 status = bci.Status | bci.Status1;
730
731 switch (status) {
732 case SECTOR_FREE:
733 /* no modification of a sector should follow a free sector */
734 goto the_end;
735 case SECTOR_DELETED:
736 lastgoodEUN = BLOCK_NIL;
737 break;
738 case SECTOR_USED:
739 lastgoodEUN = thisEUN;
740 break;
741 case SECTOR_IGNORE:
742 break;
743 default:
744 printk("Unknown status for block %ld in EUN %d: %x\n",
745 block, thisEUN, status);
746 break;
747 }
748
749 if (!silly--) {
750 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
751 block / (nftl->EraseSize / 512));
752 return 1;
753 }
754 thisEUN = nftl->ReplUnitTable[thisEUN];
755 }
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200756 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
758 the_end:
759 if (lastgoodEUN == BLOCK_NIL) {
760 /* the requested block is not on the media, return all 0x00 */
761 memset(buffer, 0, 512);
762 } else {
763 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
764 size_t retlen;
Thomas Gleixner9a1fcdf2006-05-29 14:56:39 +0200765 int res = mtd->read(mtd, ptr, 512, &retlen, buffer);
766
767 if (res < 0 && res != -EUCLEAN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 return -EIO;
769 }
770 return 0;
771}
772
773static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
774{
775 struct NFTLrecord *nftl = (void *)dev;
776
777 geo->heads = nftl->heads;
778 geo->sectors = nftl->sectors;
779 geo->cylinders = nftl->cylinders;
780
781 return 0;
782}
783
784/****************************************************************************
785 *
786 * Module stuff
787 *
788 ****************************************************************************/
789
790
791static struct mtd_blktrans_ops nftl_tr = {
792 .name = "nftl",
793 .major = NFTL_MAJOR,
794 .part_bits = NFTL_PARTN_BITS,
Richard Purdie19187672006-10-27 09:09:33 +0100795 .blksize = 512,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 .getgeo = nftl_getgeo,
797 .readsect = nftl_readblock,
798#ifdef CONFIG_NFTL_RW
799 .writesect = nftl_writeblock,
800#endif
801 .add_mtd = nftl_add_mtd,
802 .remove_dev = nftl_remove_dev,
803 .owner = THIS_MODULE,
804};
805
806extern char nftlmountrev[];
807
808static int __init init_nftl(void)
809{
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000810 printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
812 return register_mtd_blktrans(&nftl_tr);
813}
814
815static void __exit cleanup_nftl(void)
816{
817 deregister_mtd_blktrans(&nftl_tr);
818}
819
820module_init(init_nftl);
821module_exit(cleanup_nftl);
822
823MODULE_LICENSE("GPL");
824MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
825MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");