blob: f4d38546068ff6f3fc2bf9bbcc00f70ce686caa3 [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;
Richard Purdie19187672006-10-27 09:09:33 +010070
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 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;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200150
151 res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
Vitaly Wool70145682006-11-03 18:20:38 +0300152 *retlen = ops.oobretlen;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200153 return res;
154}
155
156/*
157 * Write oob data to flash
158 */
159int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
160 size_t *retlen, uint8_t *buf)
161{
162 struct mtd_oob_ops ops;
163 int res;
164
165 ops.mode = MTD_OOB_PLACE;
166 ops.ooboffs = offs & (mtd->writesize - 1);
167 ops.ooblen = len;
168 ops.oobbuf = buf;
169 ops.datbuf = NULL;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200170
171 res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
Vitaly Wool70145682006-11-03 18:20:38 +0300172 *retlen = ops.oobretlen;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200173 return res;
174}
175
Frederik Deweerdt553a8012006-10-02 09:42:25 +0100176#ifdef CONFIG_NFTL_RW
177
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200178/*
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/* Actual NFTL access routines */
200/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
201 * when the give Virtual Unit Chain
202 */
203static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
204{
205 /* For a given Virtual Unit Chain: find or create a free block and
206 add it to the chain */
207 /* We're passed the number of the last EUN in the chain, to save us from
208 having to look it up again */
209 u16 pot = nftl->LastFreeEUN;
210 int silly = nftl->nb_blocks;
211
212 /* Normally, we force a fold to happen before we run out of free blocks completely */
213 if (!desperate && nftl->numfreeEUNs < 2) {
214 DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
215 return 0xffff;
216 }
217
218 /* Scan for a free block */
219 do {
220 if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
221 nftl->LastFreeEUN = pot;
222 nftl->numfreeEUNs--;
223 return pot;
224 }
225
226 /* This will probably point to the MediaHdr unit itself,
227 right at the beginning of the partition. But that unit
228 (and the backup unit too) should have the UCI set
229 up so that it's not selected for overwriting */
230 if (++pot > nftl->lastEUN)
231 pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
232
233 if (!silly--) {
234 printk("Argh! No free blocks found! LastFreeEUN = %d, "
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000235 "FirstEUN = %d\n", nftl->LastFreeEUN,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
237 return 0xffff;
238 }
239 } while (pot != nftl->LastFreeEUN);
240
241 return 0xffff;
242}
243
244static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
245{
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200246 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 u16 BlockMap[MAX_SECTORS_PER_UNIT];
248 unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
249 unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
250 unsigned int thisEUN;
251 int block;
252 int silly;
253 unsigned int targetEUN;
254 struct nftl_oob oob;
255 int inplace = 1;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200256 size_t retlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
258 memset(BlockMap, 0xff, sizeof(BlockMap));
259 memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
260
261 thisEUN = nftl->EUNtable[thisVUC];
262
263 if (thisEUN == BLOCK_NIL) {
264 printk(KERN_WARNING "Trying to fold non-existent "
265 "Virtual Unit Chain %d!\n", thisVUC);
266 return BLOCK_NIL;
267 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000268
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 /* Scan to find the Erase Unit which holds the actual data for each
270 512-byte block within the Chain.
271 */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200272 silly = MAX_LOOPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 targetEUN = BLOCK_NIL;
274 while (thisEUN <= nftl->lastEUN ) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200275 unsigned int status, foldmark;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
277 targetEUN = thisEUN;
278 for (block = 0; block < nftl->EraseSize / 512; block ++) {
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200279 nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200280 (block * 512), 16 , &retlen,
281 (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 if (block == 2) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200283 foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
284 if (foldmark == FOLD_MARK_IN_PROGRESS) {
285 DEBUG(MTD_DEBUG_LEVEL1,
286 "Write Inhibited on EUN %d\n", thisEUN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 inplace = 0;
288 } else {
289 /* There's no other reason not to do inplace,
290 except ones that come later. So we don't need
291 to preserve inplace */
292 inplace = 1;
293 }
294 }
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200295 status = oob.b.Status | oob.b.Status1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 BlockLastState[block] = status;
297
298 switch(status) {
299 case SECTOR_FREE:
300 BlockFreeFound[block] = 1;
301 break;
302
303 case SECTOR_USED:
304 if (!BlockFreeFound[block])
305 BlockMap[block] = thisEUN;
306 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000307 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 "SECTOR_USED found after SECTOR_FREE "
309 "in Virtual Unit Chain %d for block %d\n",
310 thisVUC, block);
311 break;
312 case SECTOR_DELETED:
313 if (!BlockFreeFound[block])
314 BlockMap[block] = BLOCK_NIL;
315 else
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000316 printk(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 "SECTOR_DELETED found after SECTOR_FREE "
318 "in Virtual Unit Chain %d for block %d\n",
319 thisVUC, block);
320 break;
321
322 case SECTOR_IGNORE:
323 break;
324 default:
325 printk("Unknown status for block %d in EUN %d: %x\n",
326 block, thisEUN, status);
327 }
328 }
329
330 if (!silly--) {
331 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
332 thisVUC);
333 return BLOCK_NIL;
334 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000335
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 thisEUN = nftl->ReplUnitTable[thisEUN];
337 }
338
339 if (inplace) {
340 /* We're being asked to be a fold-in-place. Check
341 that all blocks which actually have data associated
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000342 with them (i.e. BlockMap[block] != BLOCK_NIL) are
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 either already present or SECTOR_FREE in the target
344 block. If not, we're going to have to fold out-of-place
345 anyway.
346 */
347 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
348 if (BlockLastState[block] != SECTOR_FREE &&
349 BlockMap[block] != BLOCK_NIL &&
350 BlockMap[block] != targetEUN) {
351 DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
352 "block %d was %x lastEUN, "
353 "and is in EUN %d (%s) %d\n",
354 thisVUC, block, BlockLastState[block],
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000355 BlockMap[block],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 BlockMap[block]== targetEUN ? "==" : "!=",
357 targetEUN);
358 inplace = 0;
359 break;
360 }
361 }
362
363 if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
364 pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
365 BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
366 SECTOR_FREE) {
367 DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
368 "Folding out of place.\n", targetEUN);
369 inplace = 0;
370 }
371 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 if (!inplace) {
374 DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
375 "Trying out-of-place\n", thisVUC);
376 /* We need to find a targetEUN to fold into. */
377 targetEUN = NFTL_findfreeblock(nftl, 1);
378 if (targetEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000379 /* Ouch. Now we're screwed. We need to do a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380 fold-in-place of another chain to make room
381 for this one. We need a better way of selecting
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000382 which chain to fold, because makefreeblock will
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 only ask us to fold the same one again.
384 */
385 printk(KERN_WARNING
386 "NFTL_findfreeblock(desperate) returns 0xffff.\n");
387 return BLOCK_NIL;
388 }
389 } else {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200390 /* We put a fold mark in the chain we are folding only if we
391 fold in place to help the mount check code. If we do not fold in
392 place, it is possible to find the valid chain by selecting the
393 longer one */
394 oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
395 oob.u.c.unused = 0xffffffff;
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200396 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200397 8, &retlen, (char *)&oob.u);
398 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399
400 /* OK. We now know the location of every block in the Virtual Unit Chain,
401 and the Erase Unit into which we are supposed to be copying.
402 Go for it.
403 */
404 DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
405 for (block = 0; block < nftl->EraseSize / 512 ; block++) {
406 unsigned char movebuf[512];
407 int ret;
408
409 /* If it's in the target EUN already, or if it's pending write, do nothing */
410 if (BlockMap[block] == targetEUN ||
411 (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
412 continue;
413 }
414
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200415 /* copy only in non free block (free blocks can only
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 happen in case of media errors or deleted blocks) */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200417 if (BlockMap[block] == BLOCK_NIL)
418 continue;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000419
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200420 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
421 512, &retlen, movebuf);
Thomas Gleixner9a1fcdf2006-05-29 14:56:39 +0200422 if (ret < 0 && ret != -EUCLEAN) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200423 ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
424 + (block * 512), 512, &retlen,
425 movebuf);
426 if (ret != -EIO)
427 printk("Error went away on retry.\n");
428 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 memset(&oob, 0xff, sizeof(struct nftl_oob));
430 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Thomas Gleixner9223a452006-05-23 17:21:03 +0200431
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200432 nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
433 (block * 512), 512, &retlen, movebuf, (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000435
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200436 /* add the header so that it is now a valid chain */
437 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
438 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000439
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200440 nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200441 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
443 /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
444
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000445 /* At this point, we have two different chains for this Virtual Unit, and no way to tell
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 them apart. If we crash now, we get confused. However, both contain the same data, so we
447 shouldn't actually lose data in this case. It's just that when we load up on a medium which
448 has duplicate chains, we need to free one of the chains because it's not necessary any more.
449 */
450 thisEUN = nftl->EUNtable[thisVUC];
451 DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
452
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000453 /* For each block in the old chain (except the targetEUN of course),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 free it and make it available for future use */
455 while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
456 unsigned int EUNtmp;
457
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200458 EUNtmp = nftl->ReplUnitTable[thisEUN];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200460 if (NFTL_formatblock(nftl, thisEUN) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 /* could not erase : mark block as reserved
462 */
463 nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200464 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 /* correctly erased : mark it as free */
466 nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
467 nftl->numfreeEUNs++;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200468 }
469 thisEUN = EUNtmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 /* Make this the new start of chain for thisVUC */
473 nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
474 nftl->EUNtable[thisVUC] = targetEUN;
475
476 return targetEUN;
477}
478
479static u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
480{
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000481 /* This is the part that needs some cleverness applied.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 For now, I'm doing the minimum applicable to actually
483 get the thing to work.
484 Wear-levelling and other clever stuff needs to be implemented
485 and we also need to do some assessment of the results when
486 the system loses power half-way through the routine.
487 */
488 u16 LongestChain = 0;
489 u16 ChainLength = 0, thislen;
490 u16 chain, EUN;
491
492 for (chain = 0; chain < le32_to_cpu(nftl->MediaHdr.FormattedSize) / nftl->EraseSize; chain++) {
493 EUN = nftl->EUNtable[chain];
494 thislen = 0;
495
496 while (EUN <= nftl->lastEUN) {
497 thislen++;
498 //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
499 EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
500 if (thislen > 0xff00) {
501 printk("Endless loop in Virtual Chain %d: Unit %x\n",
502 chain, EUN);
503 }
504 if (thislen > 0xff10) {
505 /* Actually, don't return failure. Just ignore this chain and
506 get on with it. */
507 thislen = 0;
508 break;
509 }
510 }
511
512 if (thislen > ChainLength) {
513 //printk("New longest chain is %d with length %d\n", chain, thislen);
514 ChainLength = thislen;
515 LongestChain = chain;
516 }
517 }
518
519 if (ChainLength < 2) {
520 printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
521 "Failing request\n");
522 return 0xffff;
523 }
524
525 return NFTL_foldchain (nftl, LongestChain, pendingblock);
526}
527
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000528/* NFTL_findwriteunit: Return the unit number into which we can write
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 for this block. Make it available if it isn't already
530*/
531static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
532{
533 u16 lastEUN;
534 u16 thisVUC = block / (nftl->EraseSize / 512);
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200535 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 unsigned int writeEUN;
537 unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
538 size_t retlen;
539 int silly, silly2 = 3;
540 struct nftl_oob oob;
541
542 do {
543 /* Scan the media to find a unit in the VUC which has
544 a free space for the block in question.
545 */
546
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000547 /* This condition catches the 0x[7f]fff cases, as well as
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 being a sanity check for past-end-of-media access
549 */
550 lastEUN = BLOCK_NIL;
551 writeEUN = nftl->EUNtable[thisVUC];
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200552 silly = MAX_LOOPS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 while (writeEUN <= nftl->lastEUN) {
554 struct nftl_bci bci;
555 size_t retlen;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200556 unsigned int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
558 lastEUN = writeEUN;
559
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200560 nftl_read_oob(mtd,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200561 (writeEUN * nftl->EraseSize) + blockofs,
562 8, &retlen, (char *)&bci);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000563
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
565 block , writeEUN, le16_to_cpu(bci.Status));
566
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200567 status = bci.Status | bci.Status1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 switch(status) {
569 case SECTOR_FREE:
570 return writeEUN;
571
572 case SECTOR_DELETED:
573 case SECTOR_USED:
574 case SECTOR_IGNORE:
575 break;
576 default:
577 // Invalid block. Don't use it any more. Must implement.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000578 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000580
581 if (!silly--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 printk(KERN_WARNING
583 "Infinite loop in Virtual Unit Chain 0x%x\n",
584 thisVUC);
585 return 0xffff;
586 }
587
588 /* Skip to next block in chain */
589 writeEUN = nftl->ReplUnitTable[writeEUN];
590 }
591
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000592 /* OK. We didn't find one in the existing chain, or there
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 is no existing chain. */
594
595 /* Try to find an already-free block */
596 writeEUN = NFTL_findfreeblock(nftl, 0);
597
598 if (writeEUN == BLOCK_NIL) {
599 /* That didn't work - there were no free blocks just
600 waiting to be picked up. We're going to have to fold
601 a chain to make room.
602 */
603
604 /* First remember the start of this chain */
605 //u16 startEUN = nftl->EUNtable[thisVUC];
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000606
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 //printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
608 writeEUN = NFTL_makefreeblock(nftl, 0xffff);
609
610 if (writeEUN == BLOCK_NIL) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000611 /* OK, we accept that the above comment is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 lying - there may have been free blocks
613 last time we called NFTL_findfreeblock(),
614 but they are reserved for when we're
615 desperate. Well, now we're desperate.
616 */
617 DEBUG(MTD_DEBUG_LEVEL1, "Using desperate==1 to find free EUN to accommodate write to VUC %d\n", thisVUC);
618 writeEUN = NFTL_findfreeblock(nftl, 1);
619 }
620 if (writeEUN == BLOCK_NIL) {
621 /* Ouch. This should never happen - we should
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000622 always be able to make some room somehow.
623 If we get here, we've allocated more storage
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 space than actual media, or our makefreeblock
625 routine is missing something.
626 */
627 printk(KERN_WARNING "Cannot make free space.\n");
628 return BLOCK_NIL;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000629 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 //printk("Restarting scan\n");
631 lastEUN = BLOCK_NIL;
632 continue;
633 }
634
635 /* We've found a free block. Insert it into the chain. */
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000636
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 if (lastEUN != BLOCK_NIL) {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200638 thisVUC |= 0x8000; /* It's a replacement block */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 } else {
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200640 /* The first block in a new chain */
641 nftl->EUNtable[thisVUC] = writeEUN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 }
643
644 /* set up the actual EUN we're writing into */
645 /* Both in our cache... */
646 nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
647
648 /* ... and on the flash itself */
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200649 nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200650 &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
652 oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
653
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200654 nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200655 &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200657 /* we link the new block to the chain only after the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 block is ready. It avoids the case where the chain
659 could point to a free block */
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200660 if (lastEUN != BLOCK_NIL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 /* Both in our cache... */
662 nftl->ReplUnitTable[lastEUN] = writeEUN;
663 /* ... and on the flash itself */
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200664 nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200665 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
667 oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
668 = cpu_to_le16(writeEUN);
669
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200670 nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200671 8, &retlen, (char *)&oob.u);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 }
673
674 return writeEUN;
675
676 } while (silly2--);
677
678 printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
679 thisVUC);
680 return 0xffff;
681}
682
683static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
684 char *buffer)
685{
686 struct NFTLrecord *nftl = (void *)mbd;
687 u16 writeEUN;
688 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
689 size_t retlen;
690 struct nftl_oob oob;
691
692 writeEUN = NFTL_findwriteunit(nftl, block);
693
694 if (writeEUN == BLOCK_NIL) {
695 printk(KERN_WARNING
696 "NFTL_writeblock(): Cannot find block to write to\n");
697 /* If we _still_ haven't got a block to use, we're screwed */
698 return 1;
699 }
700
701 memset(&oob, 0xff, sizeof(struct nftl_oob));
702 oob.b.Status = oob.b.Status1 = SECTOR_USED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200704 nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
705 512, &retlen, (char *)buffer, (char *)&oob);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 return 0;
707}
708#endif /* CONFIG_NFTL_RW */
709
710static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
711 char *buffer)
712{
713 struct NFTLrecord *nftl = (void *)mbd;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200714 struct mtd_info *mtd = nftl->mbd.mtd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 u16 lastgoodEUN;
716 u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
717 unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200718 unsigned int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 int silly = MAX_LOOPS;
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200720 size_t retlen;
721 struct nftl_bci bci;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
723 lastgoodEUN = BLOCK_NIL;
724
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200725 if (thisEUN != BLOCK_NIL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 while (thisEUN < nftl->nb_blocks) {
Thomas Gleixner8593fbc2006-05-29 03:26:58 +0200727 if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200728 blockofs, 8, &retlen,
729 (char *)&bci) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 status = SECTOR_IGNORE;
731 else
732 status = bci.Status | bci.Status1;
733
734 switch (status) {
735 case SECTOR_FREE:
736 /* no modification of a sector should follow a free sector */
737 goto the_end;
738 case SECTOR_DELETED:
739 lastgoodEUN = BLOCK_NIL;
740 break;
741 case SECTOR_USED:
742 lastgoodEUN = thisEUN;
743 break;
744 case SECTOR_IGNORE:
745 break;
746 default:
747 printk("Unknown status for block %ld in EUN %d: %x\n",
748 block, thisEUN, status);
749 break;
750 }
751
752 if (!silly--) {
753 printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
754 block / (nftl->EraseSize / 512));
755 return 1;
756 }
757 thisEUN = nftl->ReplUnitTable[thisEUN];
758 }
Thomas Gleixnerf4a43cf2006-05-28 11:01:53 +0200759 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
761 the_end:
762 if (lastgoodEUN == BLOCK_NIL) {
763 /* the requested block is not on the media, return all 0x00 */
764 memset(buffer, 0, 512);
765 } else {
766 loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
767 size_t retlen;
Thomas Gleixner9a1fcdf2006-05-29 14:56:39 +0200768 int res = mtd->read(mtd, ptr, 512, &retlen, buffer);
769
770 if (res < 0 && res != -EUCLEAN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 return -EIO;
772 }
773 return 0;
774}
775
776static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
777{
778 struct NFTLrecord *nftl = (void *)dev;
779
780 geo->heads = nftl->heads;
781 geo->sectors = nftl->sectors;
782 geo->cylinders = nftl->cylinders;
783
784 return 0;
785}
786
787/****************************************************************************
788 *
789 * Module stuff
790 *
791 ****************************************************************************/
792
793
794static struct mtd_blktrans_ops nftl_tr = {
795 .name = "nftl",
796 .major = NFTL_MAJOR,
797 .part_bits = NFTL_PARTN_BITS,
Richard Purdie19187672006-10-27 09:09:33 +0100798 .blksize = 512,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 .getgeo = nftl_getgeo,
800 .readsect = nftl_readblock,
801#ifdef CONFIG_NFTL_RW
802 .writesect = nftl_writeblock,
803#endif
804 .add_mtd = nftl_add_mtd,
805 .remove_dev = nftl_remove_dev,
806 .owner = THIS_MODULE,
807};
808
809extern char nftlmountrev[];
810
811static int __init init_nftl(void)
812{
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000813 printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.98 $, nftlmount.c %s\n", nftlmountrev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
815 return register_mtd_blktrans(&nftl_tr);
816}
817
818static void __exit cleanup_nftl(void)
819{
820 deregister_mtd_blktrans(&nftl_tr);
821}
822
823module_init(init_nftl);
824module_exit(cleanup_nftl);
825
826MODULE_LICENSE("GPL");
827MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
828MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");