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