x86: don't use gdt_page openly.
[linux-2.6/mini2440.git] / drivers / mtd / ftl.c
blob4a79b187b568870ad207eaa94e905aec5133e390
1 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
2 * $Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $
4 * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
5 * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
7 * Based on:
8 */
9 /*======================================================================
11 A Flash Translation Layer memory card driver
13 This driver implements a disk-like block device driver with an
14 apparent block size of 512 bytes for flash memory cards.
16 ftl_cs.c 1.62 2000/02/01 00:59:04
18 The contents of this file are subject to the Mozilla Public
19 License Version 1.1 (the "License"); you may not use this file
20 except in compliance with the License. You may obtain a copy of
21 the License at http://www.mozilla.org/MPL/
23 Software distributed under the License is distributed on an "AS
24 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
25 implied. See the License for the specific language governing
26 rights and limitations under the License.
28 The initial developer of the original code is David A. Hinds
29 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
30 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
32 Alternatively, the contents of this file may be used under the
33 terms of the GNU General Public License version 2 (the "GPL"), in
34 which case the provisions of the GPL are applicable instead of the
35 above. If you wish to allow the use of your version of this file
36 only under the terms of the GPL and not to allow others to use
37 your version of this file under the MPL, indicate your decision
38 by deleting the provisions above and replace them with the notice
39 and other provisions required by the GPL. If you do not delete
40 the provisions above, a recipient may use your version of this
41 file under either the MPL or the GPL.
43 LEGAL NOTE: The FTL format is patented by M-Systems. They have
44 granted a license for its use with PCMCIA devices:
46 "M-Systems grants a royalty-free, non-exclusive license under
47 any presently existing M-Systems intellectual property rights
48 necessary for the design and development of FTL-compatible
49 drivers, file systems and utilities using the data formats with
50 PCMCIA PC Cards as described in the PCMCIA Flash Translation
51 Layer (FTL) Specification."
53 Use of the FTL format for non-PCMCIA applications may be an
54 infringement of these patents. For additional information,
55 contact M-Systems (http://www.m-sys.com) directly.
57 ======================================================================*/
58 #include <linux/mtd/blktrans.h>
59 #include <linux/module.h>
60 #include <linux/mtd/mtd.h>
61 /*#define PSYCHO_DEBUG */
63 #include <linux/kernel.h>
64 #include <linux/ptrace.h>
65 #include <linux/slab.h>
66 #include <linux/string.h>
67 #include <linux/timer.h>
68 #include <linux/major.h>
69 #include <linux/fs.h>
70 #include <linux/init.h>
71 #include <linux/hdreg.h>
72 #include <linux/vmalloc.h>
73 #include <linux/blkpg.h>
74 #include <asm/uaccess.h>
76 #include <linux/mtd/ftl.h>
78 /*====================================================================*/
80 /* Parameters that can be set with 'insmod' */
81 static int shuffle_freq = 50;
82 module_param(shuffle_freq, int, 0);
84 /*====================================================================*/
86 /* Major device # for FTL device */
87 #ifndef FTL_MAJOR
88 #define FTL_MAJOR 44
89 #endif
92 /*====================================================================*/
94 /* Maximum number of separate memory devices we'll allow */
95 #define MAX_DEV 4
97 /* Maximum number of regions per device */
98 #define MAX_REGION 4
100 /* Maximum number of partitions in an FTL region */
101 #define PART_BITS 4
103 /* Maximum number of outstanding erase requests per socket */
104 #define MAX_ERASE 8
106 /* Sector size -- shouldn't need to change */
107 #define SECTOR_SIZE 512
110 /* Each memory region corresponds to a minor device */
111 typedef struct partition_t {
112 struct mtd_blktrans_dev mbd;
113 u_int32_t state;
114 u_int32_t *VirtualBlockMap;
115 u_int32_t *VirtualPageMap;
116 u_int32_t FreeTotal;
117 struct eun_info_t {
118 u_int32_t Offset;
119 u_int32_t EraseCount;
120 u_int32_t Free;
121 u_int32_t Deleted;
122 } *EUNInfo;
123 struct xfer_info_t {
124 u_int32_t Offset;
125 u_int32_t EraseCount;
126 u_int16_t state;
127 } *XferInfo;
128 u_int16_t bam_index;
129 u_int32_t *bam_cache;
130 u_int16_t DataUnits;
131 u_int32_t BlocksPerUnit;
132 erase_unit_header_t header;
133 #if 0
134 region_info_t region;
135 memory_handle_t handle;
136 #endif
137 } partition_t;
139 /* Partition state flags */
140 #define FTL_FORMATTED 0x01
142 /* Transfer unit states */
143 #define XFER_UNKNOWN 0x00
144 #define XFER_ERASING 0x01
145 #define XFER_ERASED 0x02
146 #define XFER_PREPARED 0x03
147 #define XFER_FAILED 0x04
149 /*====================================================================*/
152 static void ftl_erase_callback(struct erase_info *done);
155 /*======================================================================
157 Scan_header() checks to see if a memory region contains an FTL
158 partition. build_maps() reads all the erase unit headers, builds
159 the erase unit map, and then builds the virtual page map.
161 ======================================================================*/
163 static int scan_header(partition_t *part)
165 erase_unit_header_t header;
166 loff_t offset, max_offset;
167 size_t ret;
168 int err;
169 part->header.FormattedSize = 0;
170 max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
171 /* Search first megabyte for a valid FTL header */
172 for (offset = 0;
173 (offset + sizeof(header)) < max_offset;
174 offset += part->mbd.mtd->erasesize ? : 0x2000) {
176 err = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret,
177 (unsigned char *)&header);
179 if (err)
180 return err;
182 if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
185 if (offset == max_offset) {
186 printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
187 return -ENOENT;
189 if (header.BlockSize != 9 ||
190 (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
191 (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
192 printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
193 return -1;
195 if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
196 printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
197 1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
198 return -1;
200 part->header = header;
201 return 0;
204 static int build_maps(partition_t *part)
206 erase_unit_header_t header;
207 u_int16_t xvalid, xtrans, i;
208 u_int blocks, j;
209 int hdr_ok, ret = -1;
210 ssize_t retval;
211 loff_t offset;
213 /* Set up erase unit maps */
214 part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
215 part->header.NumTransferUnits;
216 part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t),
217 GFP_KERNEL);
218 if (!part->EUNInfo)
219 goto out;
220 for (i = 0; i < part->DataUnits; i++)
221 part->EUNInfo[i].Offset = 0xffffffff;
222 part->XferInfo =
223 kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t),
224 GFP_KERNEL);
225 if (!part->XferInfo)
226 goto out_EUNInfo;
228 xvalid = xtrans = 0;
229 for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
230 offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
231 << part->header.EraseUnitSize);
232 ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval,
233 (unsigned char *)&header);
235 if (ret)
236 goto out_XferInfo;
238 ret = -1;
239 /* Is this a transfer partition? */
240 hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
241 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
242 (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
243 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
244 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
245 le32_to_cpu(header.EraseCount);
246 xvalid++;
247 } else {
248 if (xtrans == part->header.NumTransferUnits) {
249 printk(KERN_NOTICE "ftl_cs: format error: too many "
250 "transfer units!\n");
251 goto out_XferInfo;
253 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
254 part->XferInfo[xtrans].state = XFER_PREPARED;
255 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
256 } else {
257 part->XferInfo[xtrans].state = XFER_UNKNOWN;
258 /* Pick anything reasonable for the erase count */
259 part->XferInfo[xtrans].EraseCount =
260 le32_to_cpu(part->header.EraseCount);
262 part->XferInfo[xtrans].Offset = offset;
263 xtrans++;
266 /* Check for format trouble */
267 header = part->header;
268 if ((xtrans != header.NumTransferUnits) ||
269 (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
270 printk(KERN_NOTICE "ftl_cs: format error: erase units "
271 "don't add up!\n");
272 goto out_XferInfo;
275 /* Set up virtual page map */
276 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
277 part->VirtualBlockMap = vmalloc(blocks * sizeof(u_int32_t));
278 if (!part->VirtualBlockMap)
279 goto out_XferInfo;
281 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(u_int32_t));
282 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
284 part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(u_int32_t),
285 GFP_KERNEL);
286 if (!part->bam_cache)
287 goto out_VirtualBlockMap;
289 part->bam_index = 0xffff;
290 part->FreeTotal = 0;
292 for (i = 0; i < part->DataUnits; i++) {
293 part->EUNInfo[i].Free = 0;
294 part->EUNInfo[i].Deleted = 0;
295 offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
297 ret = part->mbd.mtd->read(part->mbd.mtd, offset,
298 part->BlocksPerUnit * sizeof(u_int32_t), &retval,
299 (unsigned char *)part->bam_cache);
301 if (ret)
302 goto out_bam_cache;
304 for (j = 0; j < part->BlocksPerUnit; j++) {
305 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
306 part->EUNInfo[i].Free++;
307 part->FreeTotal++;
308 } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
309 (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
310 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
311 (i << header.EraseUnitSize) + (j << header.BlockSize);
312 else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
313 part->EUNInfo[i].Deleted++;
317 ret = 0;
318 goto out;
320 out_bam_cache:
321 kfree(part->bam_cache);
322 out_VirtualBlockMap:
323 vfree(part->VirtualBlockMap);
324 out_XferInfo:
325 kfree(part->XferInfo);
326 out_EUNInfo:
327 kfree(part->EUNInfo);
328 out:
329 return ret;
330 } /* build_maps */
332 /*======================================================================
334 Erase_xfer() schedules an asynchronous erase operation for a
335 transfer unit.
337 ======================================================================*/
339 static int erase_xfer(partition_t *part,
340 u_int16_t xfernum)
342 int ret;
343 struct xfer_info_t *xfer;
344 struct erase_info *erase;
346 xfer = &part->XferInfo[xfernum];
347 DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
348 xfer->state = XFER_ERASING;
350 /* Is there a free erase slot? Always in MTD. */
353 erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
354 if (!erase)
355 return -ENOMEM;
357 erase->mtd = part->mbd.mtd;
358 erase->callback = ftl_erase_callback;
359 erase->addr = xfer->Offset;
360 erase->len = 1 << part->header.EraseUnitSize;
361 erase->priv = (u_long)part;
363 ret = part->mbd.mtd->erase(part->mbd.mtd, erase);
365 if (!ret)
366 xfer->EraseCount++;
367 else
368 kfree(erase);
370 return ret;
371 } /* erase_xfer */
373 /*======================================================================
375 Prepare_xfer() takes a freshly erased transfer unit and gives
376 it an appropriate header.
378 ======================================================================*/
380 static void ftl_erase_callback(struct erase_info *erase)
382 partition_t *part;
383 struct xfer_info_t *xfer;
384 int i;
386 /* Look up the transfer unit */
387 part = (partition_t *)(erase->priv);
389 for (i = 0; i < part->header.NumTransferUnits; i++)
390 if (part->XferInfo[i].Offset == erase->addr) break;
392 if (i == part->header.NumTransferUnits) {
393 printk(KERN_NOTICE "ftl_cs: internal error: "
394 "erase lookup failed!\n");
395 return;
398 xfer = &part->XferInfo[i];
399 if (erase->state == MTD_ERASE_DONE)
400 xfer->state = XFER_ERASED;
401 else {
402 xfer->state = XFER_FAILED;
403 printk(KERN_NOTICE "ftl_cs: erase failed: state = %d\n",
404 erase->state);
407 kfree(erase);
409 } /* ftl_erase_callback */
411 static int prepare_xfer(partition_t *part, int i)
413 erase_unit_header_t header;
414 struct xfer_info_t *xfer;
415 int nbam, ret;
416 u_int32_t ctl;
417 ssize_t retlen;
418 loff_t offset;
420 xfer = &part->XferInfo[i];
421 xfer->state = XFER_FAILED;
423 DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
425 /* Write the transfer unit header */
426 header = part->header;
427 header.LogicalEUN = cpu_to_le16(0xffff);
428 header.EraseCount = cpu_to_le32(xfer->EraseCount);
430 ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header),
431 &retlen, (u_char *)&header);
433 if (ret) {
434 return ret;
437 /* Write the BAM stub */
438 nbam = (part->BlocksPerUnit * sizeof(u_int32_t) +
439 le32_to_cpu(part->header.BAMOffset) + SECTOR_SIZE - 1) / SECTOR_SIZE;
441 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
442 ctl = cpu_to_le32(BLOCK_CONTROL);
444 for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
446 ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
447 &retlen, (u_char *)&ctl);
449 if (ret)
450 return ret;
452 xfer->state = XFER_PREPARED;
453 return 0;
455 } /* prepare_xfer */
457 /*======================================================================
459 Copy_erase_unit() takes a full erase block and a transfer unit,
460 copies everything to the transfer unit, then swaps the block
461 pointers.
463 All data blocks are copied to the corresponding blocks in the
464 target unit, so the virtual block map does not need to be
465 updated.
467 ======================================================================*/
469 static int copy_erase_unit(partition_t *part, u_int16_t srcunit,
470 u_int16_t xferunit)
472 u_char buf[SECTOR_SIZE];
473 struct eun_info_t *eun;
474 struct xfer_info_t *xfer;
475 u_int32_t src, dest, free, i;
476 u_int16_t unit;
477 int ret;
478 ssize_t retlen;
479 loff_t offset;
480 u_int16_t srcunitswap = cpu_to_le16(srcunit);
482 eun = &part->EUNInfo[srcunit];
483 xfer = &part->XferInfo[xferunit];
484 DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
485 eun->Offset, xfer->Offset);
488 /* Read current BAM */
489 if (part->bam_index != srcunit) {
491 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
493 ret = part->mbd.mtd->read(part->mbd.mtd, offset,
494 part->BlocksPerUnit * sizeof(u_int32_t),
495 &retlen, (u_char *) (part->bam_cache));
497 /* mark the cache bad, in case we get an error later */
498 part->bam_index = 0xffff;
500 if (ret) {
501 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
502 return ret;
506 /* Write the LogicalEUN for the transfer unit */
507 xfer->state = XFER_UNKNOWN;
508 offset = xfer->Offset + 20; /* Bad! */
509 unit = cpu_to_le16(0x7fff);
511 ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t),
512 &retlen, (u_char *) &unit);
514 if (ret) {
515 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
516 return ret;
519 /* Copy all data blocks from source unit to transfer unit */
520 src = eun->Offset; dest = xfer->Offset;
522 free = 0;
523 ret = 0;
524 for (i = 0; i < part->BlocksPerUnit; i++) {
525 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
526 case BLOCK_CONTROL:
527 /* This gets updated later */
528 break;
529 case BLOCK_DATA:
530 case BLOCK_REPLACEMENT:
531 ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE,
532 &retlen, (u_char *) buf);
533 if (ret) {
534 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
535 return ret;
539 ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE,
540 &retlen, (u_char *) buf);
541 if (ret) {
542 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
543 return ret;
546 break;
547 default:
548 /* All other blocks must be free */
549 part->bam_cache[i] = cpu_to_le32(0xffffffff);
550 free++;
551 break;
553 src += SECTOR_SIZE;
554 dest += SECTOR_SIZE;
557 /* Write the BAM to the transfer unit */
558 ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset),
559 part->BlocksPerUnit * sizeof(int32_t), &retlen,
560 (u_char *)part->bam_cache);
561 if (ret) {
562 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
563 return ret;
567 /* All clear? Then update the LogicalEUN again */
568 ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t),
569 &retlen, (u_char *)&srcunitswap);
571 if (ret) {
572 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
573 return ret;
577 /* Update the maps and usage stats*/
578 i = xfer->EraseCount;
579 xfer->EraseCount = eun->EraseCount;
580 eun->EraseCount = i;
581 i = xfer->Offset;
582 xfer->Offset = eun->Offset;
583 eun->Offset = i;
584 part->FreeTotal -= eun->Free;
585 part->FreeTotal += free;
586 eun->Free = free;
587 eun->Deleted = 0;
589 /* Now, the cache should be valid for the new block */
590 part->bam_index = srcunit;
592 return 0;
593 } /* copy_erase_unit */
595 /*======================================================================
597 reclaim_block() picks a full erase unit and a transfer unit and
598 then calls copy_erase_unit() to copy one to the other. Then, it
599 schedules an erase on the expired block.
601 What's a good way to decide which transfer unit and which erase
602 unit to use? Beats me. My way is to always pick the transfer
603 unit with the fewest erases, and usually pick the data unit with
604 the most deleted blocks. But with a small probability, pick the
605 oldest data unit instead. This means that we generally postpone
606 the next reclaimation as long as possible, but shuffle static
607 stuff around a bit for wear leveling.
609 ======================================================================*/
611 static int reclaim_block(partition_t *part)
613 u_int16_t i, eun, xfer;
614 u_int32_t best;
615 int queued, ret;
617 DEBUG(0, "ftl_cs: reclaiming space...\n");
618 DEBUG(3, "NumTransferUnits == %x\n", part->header.NumTransferUnits);
619 /* Pick the least erased transfer unit */
620 best = 0xffffffff; xfer = 0xffff;
621 do {
622 queued = 0;
623 for (i = 0; i < part->header.NumTransferUnits; i++) {
624 int n=0;
625 if (part->XferInfo[i].state == XFER_UNKNOWN) {
626 DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i);
627 n=1;
628 erase_xfer(part, i);
630 if (part->XferInfo[i].state == XFER_ERASING) {
631 DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i);
632 n=1;
633 queued = 1;
635 else if (part->XferInfo[i].state == XFER_ERASED) {
636 DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i);
637 n=1;
638 prepare_xfer(part, i);
640 if (part->XferInfo[i].state == XFER_PREPARED) {
641 DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i);
642 n=1;
643 if (part->XferInfo[i].EraseCount <= best) {
644 best = part->XferInfo[i].EraseCount;
645 xfer = i;
648 if (!n)
649 DEBUG(3,"XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
652 if (xfer == 0xffff) {
653 if (queued) {
654 DEBUG(1, "ftl_cs: waiting for transfer "
655 "unit to be prepared...\n");
656 if (part->mbd.mtd->sync)
657 part->mbd.mtd->sync(part->mbd.mtd);
658 } else {
659 static int ne = 0;
660 if (++ne < 5)
661 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
662 "suitable transfer units!\n");
663 else
664 DEBUG(1, "ftl_cs: reclaim failed: no "
665 "suitable transfer units!\n");
667 return -EIO;
670 } while (xfer == 0xffff);
672 eun = 0;
673 if ((jiffies % shuffle_freq) == 0) {
674 DEBUG(1, "ftl_cs: recycling freshest block...\n");
675 best = 0xffffffff;
676 for (i = 0; i < part->DataUnits; i++)
677 if (part->EUNInfo[i].EraseCount <= best) {
678 best = part->EUNInfo[i].EraseCount;
679 eun = i;
681 } else {
682 best = 0;
683 for (i = 0; i < part->DataUnits; i++)
684 if (part->EUNInfo[i].Deleted >= best) {
685 best = part->EUNInfo[i].Deleted;
686 eun = i;
688 if (best == 0) {
689 static int ne = 0;
690 if (++ne < 5)
691 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
692 "no free blocks!\n");
693 else
694 DEBUG(1,"ftl_cs: reclaim failed: "
695 "no free blocks!\n");
697 return -EIO;
700 ret = copy_erase_unit(part, eun, xfer);
701 if (!ret)
702 erase_xfer(part, xfer);
703 else
704 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
705 return ret;
706 } /* reclaim_block */
708 /*======================================================================
710 Find_free() searches for a free block. If necessary, it updates
711 the BAM cache for the erase unit containing the free block. It
712 returns the block index -- the erase unit is just the currently
713 cached unit. If there are no free blocks, it returns 0 -- this
714 is never a valid data block because it contains the header.
716 ======================================================================*/
718 #ifdef PSYCHO_DEBUG
719 static void dump_lists(partition_t *part)
721 int i;
722 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
723 for (i = 0; i < part->DataUnits; i++)
724 printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, "
725 "%d deleted\n", i,
726 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
727 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
729 #endif
731 static u_int32_t find_free(partition_t *part)
733 u_int16_t stop, eun;
734 u_int32_t blk;
735 size_t retlen;
736 int ret;
738 /* Find an erase unit with some free space */
739 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
740 eun = stop;
741 do {
742 if (part->EUNInfo[eun].Free != 0) break;
743 /* Wrap around at end of table */
744 if (++eun == part->DataUnits) eun = 0;
745 } while (eun != stop);
747 if (part->EUNInfo[eun].Free == 0)
748 return 0;
750 /* Is this unit's BAM cached? */
751 if (eun != part->bam_index) {
752 /* Invalidate cache */
753 part->bam_index = 0xffff;
755 ret = part->mbd.mtd->read(part->mbd.mtd,
756 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
757 part->BlocksPerUnit * sizeof(u_int32_t),
758 &retlen, (u_char *) (part->bam_cache));
760 if (ret) {
761 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
762 return 0;
764 part->bam_index = eun;
767 /* Find a free block */
768 for (blk = 0; blk < part->BlocksPerUnit; blk++)
769 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
770 if (blk == part->BlocksPerUnit) {
771 #ifdef PSYCHO_DEBUG
772 static int ne = 0;
773 if (++ne == 1)
774 dump_lists(part);
775 #endif
776 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
777 return 0;
779 DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk, eun);
780 return blk;
782 } /* find_free */
785 /*======================================================================
787 Read a series of sectors from an FTL partition.
789 ======================================================================*/
791 static int ftl_read(partition_t *part, caddr_t buffer,
792 u_long sector, u_long nblocks)
794 u_int32_t log_addr, bsize;
795 u_long i;
796 int ret;
797 size_t offset, retlen;
799 DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
800 part, sector, nblocks);
801 if (!(part->state & FTL_FORMATTED)) {
802 printk(KERN_NOTICE "ftl_cs: bad partition\n");
803 return -EIO;
805 bsize = 1 << part->header.EraseUnitSize;
807 for (i = 0; i < nblocks; i++) {
808 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
809 printk(KERN_NOTICE "ftl_cs: bad read offset\n");
810 return -EIO;
812 log_addr = part->VirtualBlockMap[sector+i];
813 if (log_addr == 0xffffffff)
814 memset(buffer, 0, SECTOR_SIZE);
815 else {
816 offset = (part->EUNInfo[log_addr / bsize].Offset
817 + (log_addr % bsize));
818 ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE,
819 &retlen, (u_char *) buffer);
821 if (ret) {
822 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
823 return ret;
826 buffer += SECTOR_SIZE;
828 return 0;
829 } /* ftl_read */
831 /*======================================================================
833 Write a series of sectors to an FTL partition
835 ======================================================================*/
837 static int set_bam_entry(partition_t *part, u_int32_t log_addr,
838 u_int32_t virt_addr)
840 u_int32_t bsize, blk, le_virt_addr;
841 #ifdef PSYCHO_DEBUG
842 u_int32_t old_addr;
843 #endif
844 u_int16_t eun;
845 int ret;
846 size_t retlen, offset;
848 DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
849 part, log_addr, virt_addr);
850 bsize = 1 << part->header.EraseUnitSize;
851 eun = log_addr / bsize;
852 blk = (log_addr % bsize) / SECTOR_SIZE;
853 offset = (part->EUNInfo[eun].Offset + blk * sizeof(u_int32_t) +
854 le32_to_cpu(part->header.BAMOffset));
856 #ifdef PSYCHO_DEBUG
857 ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t),
858 &retlen, (u_char *)&old_addr);
859 if (ret) {
860 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
861 return ret;
863 old_addr = le32_to_cpu(old_addr);
865 if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
866 ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
867 (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
868 static int ne = 0;
869 if (++ne < 5) {
870 printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
871 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x"
872 ", new = 0x%x\n", log_addr, old_addr, virt_addr);
874 return -EIO;
876 #endif
877 le_virt_addr = cpu_to_le32(virt_addr);
878 if (part->bam_index == eun) {
879 #ifdef PSYCHO_DEBUG
880 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
881 static int ne = 0;
882 if (++ne < 5) {
883 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
884 "inconsistency!\n");
885 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
886 " = 0x%x\n",
887 le32_to_cpu(part->bam_cache[blk]), old_addr);
889 return -EIO;
891 #endif
892 part->bam_cache[blk] = le_virt_addr;
894 ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
895 &retlen, (u_char *)&le_virt_addr);
897 if (ret) {
898 printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
899 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n",
900 log_addr, virt_addr);
902 return ret;
903 } /* set_bam_entry */
905 static int ftl_write(partition_t *part, caddr_t buffer,
906 u_long sector, u_long nblocks)
908 u_int32_t bsize, log_addr, virt_addr, old_addr, blk;
909 u_long i;
910 int ret;
911 size_t retlen, offset;
913 DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
914 part, sector, nblocks);
915 if (!(part->state & FTL_FORMATTED)) {
916 printk(KERN_NOTICE "ftl_cs: bad partition\n");
917 return -EIO;
919 /* See if we need to reclaim space, before we start */
920 while (part->FreeTotal < nblocks) {
921 ret = reclaim_block(part);
922 if (ret)
923 return ret;
926 bsize = 1 << part->header.EraseUnitSize;
928 virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
929 for (i = 0; i < nblocks; i++) {
930 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
931 printk(KERN_NOTICE "ftl_cs: bad write offset\n");
932 return -EIO;
935 /* Grab a free block */
936 blk = find_free(part);
937 if (blk == 0) {
938 static int ne = 0;
939 if (++ne < 5)
940 printk(KERN_NOTICE "ftl_cs: internal error: "
941 "no free blocks!\n");
942 return -ENOSPC;
945 /* Tag the BAM entry, and write the new block */
946 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
947 part->EUNInfo[part->bam_index].Free--;
948 part->FreeTotal--;
949 if (set_bam_entry(part, log_addr, 0xfffffffe))
950 return -EIO;
951 part->EUNInfo[part->bam_index].Deleted++;
952 offset = (part->EUNInfo[part->bam_index].Offset +
953 blk * SECTOR_SIZE);
954 ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
955 buffer);
957 if (ret) {
958 printk(KERN_NOTICE "ftl_cs: block write failed!\n");
959 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr"
960 " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
961 offset);
962 return -EIO;
965 /* Only delete the old entry when the new entry is ready */
966 old_addr = part->VirtualBlockMap[sector+i];
967 if (old_addr != 0xffffffff) {
968 part->VirtualBlockMap[sector+i] = 0xffffffff;
969 part->EUNInfo[old_addr/bsize].Deleted++;
970 if (set_bam_entry(part, old_addr, 0))
971 return -EIO;
974 /* Finally, set up the new pointers */
975 if (set_bam_entry(part, log_addr, virt_addr))
976 return -EIO;
977 part->VirtualBlockMap[sector+i] = log_addr;
978 part->EUNInfo[part->bam_index].Deleted--;
980 buffer += SECTOR_SIZE;
981 virt_addr += SECTOR_SIZE;
983 return 0;
984 } /* ftl_write */
986 static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
988 partition_t *part = (void *)dev;
989 u_long sect;
991 /* Sort of arbitrary: round size down to 4KiB boundary */
992 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
994 geo->heads = 1;
995 geo->sectors = 8;
996 geo->cylinders = sect >> 3;
998 return 0;
1001 static int ftl_readsect(struct mtd_blktrans_dev *dev,
1002 unsigned long block, char *buf)
1004 return ftl_read((void *)dev, buf, block, 1);
1007 static int ftl_writesect(struct mtd_blktrans_dev *dev,
1008 unsigned long block, char *buf)
1010 return ftl_write((void *)dev, buf, block, 1);
1013 /*====================================================================*/
1015 static void ftl_freepart(partition_t *part)
1017 vfree(part->VirtualBlockMap);
1018 part->VirtualBlockMap = NULL;
1019 kfree(part->VirtualPageMap);
1020 part->VirtualPageMap = NULL;
1021 kfree(part->EUNInfo);
1022 part->EUNInfo = NULL;
1023 kfree(part->XferInfo);
1024 part->XferInfo = NULL;
1025 kfree(part->bam_cache);
1026 part->bam_cache = NULL;
1027 } /* ftl_freepart */
1029 static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1031 partition_t *partition;
1033 partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
1035 if (!partition) {
1036 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1037 mtd->name);
1038 return;
1041 partition->mbd.mtd = mtd;
1043 if ((scan_header(partition) == 0) &&
1044 (build_maps(partition) == 0)) {
1046 partition->state = FTL_FORMATTED;
1047 #ifdef PCMCIA_DEBUG
1048 printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1049 le32_to_cpu(partition->header.FormattedSize) >> 10);
1050 #endif
1051 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
1053 partition->mbd.tr = tr;
1054 partition->mbd.devnum = -1;
1055 if (!add_mtd_blktrans_dev((void *)partition))
1056 return;
1059 ftl_freepart(partition);
1060 kfree(partition);
1063 static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1065 del_mtd_blktrans_dev(dev);
1066 ftl_freepart((partition_t *)dev);
1067 kfree(dev);
1070 static struct mtd_blktrans_ops ftl_tr = {
1071 .name = "ftl",
1072 .major = FTL_MAJOR,
1073 .part_bits = PART_BITS,
1074 .blksize = SECTOR_SIZE,
1075 .readsect = ftl_readsect,
1076 .writesect = ftl_writesect,
1077 .getgeo = ftl_getgeo,
1078 .add_mtd = ftl_add_mtd,
1079 .remove_dev = ftl_remove_dev,
1080 .owner = THIS_MODULE,
1083 static int init_ftl(void)
1085 DEBUG(0, "$Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $\n");
1087 return register_mtd_blktrans(&ftl_tr);
1090 static void __exit cleanup_ftl(void)
1092 deregister_mtd_blktrans(&ftl_tr);
1095 module_init(init_ftl);
1096 module_exit(cleanup_ftl);
1099 MODULE_LICENSE("Dual MPL/GPL");
1100 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1101 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");