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
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/sched.h>
65 #include <linux/ptrace.h>
66 #include <linux/slab.h>
67 #include <linux/string.h>
68 #include <linux/timer.h>
69 #include <linux/major.h>
71 #include <linux/init.h>
72 #include <linux/hdreg.h>
73 #include <linux/vmalloc.h>
74 #include <linux/blkpg.h>
75 #include <asm/uaccess.h>
77 #include <linux/mtd/ftl.h>
79 /*====================================================================*/
81 /* Parameters that can be set with 'insmod' */
82 static int shuffle_freq
= 50;
83 module_param(shuffle_freq
, int, 0);
85 /*====================================================================*/
87 /* Major device # for FTL device */
93 /*====================================================================*/
95 /* Maximum number of separate memory devices we'll allow */
98 /* Maximum number of regions per device */
101 /* Maximum number of partitions in an FTL region */
104 /* Maximum number of outstanding erase requests per socket */
107 /* Sector size -- shouldn't need to change */
108 #define SECTOR_SIZE 512
111 /* Each memory region corresponds to a minor device */
112 typedef struct partition_t
{
113 struct mtd_blktrans_dev mbd
;
115 u_int32_t
*VirtualBlockMap
;
116 u_int32_t
*VirtualPageMap
;
120 u_int32_t EraseCount
;
126 u_int32_t EraseCount
;
130 u_int32_t
*bam_cache
;
132 u_int32_t BlocksPerUnit
;
133 erase_unit_header_t header
;
135 region_info_t region
;
136 memory_handle_t handle
;
140 void ftl_freepart(partition_t
*part
);
142 /* Partition state flags */
143 #define FTL_FORMATTED 0x01
145 /* Transfer unit states */
146 #define XFER_UNKNOWN 0x00
147 #define XFER_ERASING 0x01
148 #define XFER_ERASED 0x02
149 #define XFER_PREPARED 0x03
150 #define XFER_FAILED 0x04
152 /*====================================================================*/
155 static void ftl_erase_callback(struct erase_info
*done
);
158 /*======================================================================
160 Scan_header() checks to see if a memory region contains an FTL
161 partition. build_maps() reads all the erase unit headers, builds
162 the erase unit map, and then builds the virtual page map.
164 ======================================================================*/
166 static int scan_header(partition_t
*part
)
168 erase_unit_header_t header
;
169 loff_t offset
, max_offset
;
172 part
->header
.FormattedSize
= 0;
173 max_offset
= (0x100000<part
->mbd
.mtd
->size
)?0x100000:part
->mbd
.mtd
->size
;
174 /* Search first megabyte for a valid FTL header */
176 (offset
+ sizeof(header
)) < max_offset
;
177 offset
+= part
->mbd
.mtd
->erasesize
? : 0x2000) {
179 err
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, sizeof(header
), &ret
,
180 (unsigned char *)&header
);
185 if (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0) break;
188 if (offset
== max_offset
) {
189 printk(KERN_NOTICE
"ftl_cs: FTL header not found.\n");
192 if (header
.BlockSize
!= 9 ||
193 (header
.EraseUnitSize
< 10) || (header
.EraseUnitSize
> 31) ||
194 (header
.NumTransferUnits
>= le16_to_cpu(header
.NumEraseUnits
))) {
195 printk(KERN_NOTICE
"ftl_cs: FTL header corrupt!\n");
198 if ((1 << header
.EraseUnitSize
) != part
->mbd
.mtd
->erasesize
) {
199 printk(KERN_NOTICE
"ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
200 1 << header
.EraseUnitSize
,part
->mbd
.mtd
->erasesize
);
203 part
->header
= header
;
207 static int build_maps(partition_t
*part
)
209 erase_unit_header_t header
;
210 u_int16_t xvalid
, xtrans
, i
;
212 int hdr_ok
, ret
= -1;
216 /* Set up erase unit maps */
217 part
->DataUnits
= le16_to_cpu(part
->header
.NumEraseUnits
) -
218 part
->header
.NumTransferUnits
;
219 part
->EUNInfo
= kmalloc(part
->DataUnits
* sizeof(struct eun_info_t
),
223 for (i
= 0; i
< part
->DataUnits
; i
++)
224 part
->EUNInfo
[i
].Offset
= 0xffffffff;
226 kmalloc(part
->header
.NumTransferUnits
* sizeof(struct xfer_info_t
),
232 for (i
= 0; i
< le16_to_cpu(part
->header
.NumEraseUnits
); i
++) {
233 offset
= ((i
+ le16_to_cpu(part
->header
.FirstPhysicalEUN
))
234 << part
->header
.EraseUnitSize
);
235 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, sizeof(header
), &retval
,
236 (unsigned char *)&header
);
242 /* Is this a transfer partition? */
243 hdr_ok
= (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0);
244 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) < part
->DataUnits
) &&
245 (part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
== 0xffffffff)) {
246 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
= offset
;
247 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].EraseCount
=
248 le32_to_cpu(header
.EraseCount
);
251 if (xtrans
== part
->header
.NumTransferUnits
) {
252 printk(KERN_NOTICE
"ftl_cs: format error: too many "
253 "transfer units!\n");
256 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) == 0xffff)) {
257 part
->XferInfo
[xtrans
].state
= XFER_PREPARED
;
258 part
->XferInfo
[xtrans
].EraseCount
= le32_to_cpu(header
.EraseCount
);
260 part
->XferInfo
[xtrans
].state
= XFER_UNKNOWN
;
261 /* Pick anything reasonable for the erase count */
262 part
->XferInfo
[xtrans
].EraseCount
=
263 le32_to_cpu(part
->header
.EraseCount
);
265 part
->XferInfo
[xtrans
].Offset
= offset
;
269 /* Check for format trouble */
270 header
= part
->header
;
271 if ((xtrans
!= header
.NumTransferUnits
) ||
272 (xvalid
+xtrans
!= le16_to_cpu(header
.NumEraseUnits
))) {
273 printk(KERN_NOTICE
"ftl_cs: format error: erase units "
278 /* Set up virtual page map */
279 blocks
= le32_to_cpu(header
.FormattedSize
) >> header
.BlockSize
;
280 part
->VirtualBlockMap
= vmalloc(blocks
* sizeof(u_int32_t
));
281 if (!part
->VirtualBlockMap
)
284 memset(part
->VirtualBlockMap
, 0xff, blocks
* sizeof(u_int32_t
));
285 part
->BlocksPerUnit
= (1 << header
.EraseUnitSize
) >> header
.BlockSize
;
287 part
->bam_cache
= kmalloc(part
->BlocksPerUnit
* sizeof(u_int32_t
),
289 if (!part
->bam_cache
)
290 goto out_VirtualBlockMap
;
292 part
->bam_index
= 0xffff;
295 for (i
= 0; i
< part
->DataUnits
; i
++) {
296 part
->EUNInfo
[i
].Free
= 0;
297 part
->EUNInfo
[i
].Deleted
= 0;
298 offset
= part
->EUNInfo
[i
].Offset
+ le32_to_cpu(header
.BAMOffset
);
300 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
,
301 part
->BlocksPerUnit
* sizeof(u_int32_t
), &retval
,
302 (unsigned char *)part
->bam_cache
);
307 for (j
= 0; j
< part
->BlocksPerUnit
; j
++) {
308 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[j
]))) {
309 part
->EUNInfo
[i
].Free
++;
311 } else if ((BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[j
])) == BLOCK_DATA
) &&
312 (BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
])) < blocks
))
313 part
->VirtualBlockMap
[BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
]))] =
314 (i
<< header
.EraseUnitSize
) + (j
<< header
.BlockSize
);
315 else if (BLOCK_DELETED(le32_to_cpu(part
->bam_cache
[j
])))
316 part
->EUNInfo
[i
].Deleted
++;
324 kfree(part
->bam_cache
);
326 vfree(part
->VirtualBlockMap
);
328 kfree(part
->XferInfo
);
330 kfree(part
->EUNInfo
);
335 /*======================================================================
337 Erase_xfer() schedules an asynchronous erase operation for a
340 ======================================================================*/
342 static int erase_xfer(partition_t
*part
,
346 struct xfer_info_t
*xfer
;
347 struct erase_info
*erase
;
349 xfer
= &part
->XferInfo
[xfernum
];
350 DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer
->Offset
);
351 xfer
->state
= XFER_ERASING
;
353 /* Is there a free erase slot? Always in MTD. */
356 erase
=kmalloc(sizeof(struct erase_info
), GFP_KERNEL
);
360 erase
->mtd
= part
->mbd
.mtd
;
361 erase
->callback
= ftl_erase_callback
;
362 erase
->addr
= xfer
->Offset
;
363 erase
->len
= 1 << part
->header
.EraseUnitSize
;
364 erase
->priv
= (u_long
)part
;
366 ret
= part
->mbd
.mtd
->erase(part
->mbd
.mtd
, erase
);
376 /*======================================================================
378 Prepare_xfer() takes a freshly erased transfer unit and gives
379 it an appropriate header.
381 ======================================================================*/
383 static void ftl_erase_callback(struct erase_info
*erase
)
386 struct xfer_info_t
*xfer
;
389 /* Look up the transfer unit */
390 part
= (partition_t
*)(erase
->priv
);
392 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++)
393 if (part
->XferInfo
[i
].Offset
== erase
->addr
) break;
395 if (i
== part
->header
.NumTransferUnits
) {
396 printk(KERN_NOTICE
"ftl_cs: internal error: "
397 "erase lookup failed!\n");
401 xfer
= &part
->XferInfo
[i
];
402 if (erase
->state
== MTD_ERASE_DONE
)
403 xfer
->state
= XFER_ERASED
;
405 xfer
->state
= XFER_FAILED
;
406 printk(KERN_NOTICE
"ftl_cs: erase failed: state = %d\n",
412 } /* ftl_erase_callback */
414 static int prepare_xfer(partition_t
*part
, int i
)
416 erase_unit_header_t header
;
417 struct xfer_info_t
*xfer
;
423 xfer
= &part
->XferInfo
[i
];
424 xfer
->state
= XFER_FAILED
;
426 DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer
->Offset
);
428 /* Write the transfer unit header */
429 header
= part
->header
;
430 header
.LogicalEUN
= cpu_to_le16(0xffff);
431 header
.EraseCount
= cpu_to_le32(xfer
->EraseCount
);
433 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, xfer
->Offset
, sizeof(header
),
434 &retlen
, (u_char
*)&header
);
440 /* Write the BAM stub */
441 nbam
= (part
->BlocksPerUnit
* sizeof(u_int32_t
) +
442 le32_to_cpu(part
->header
.BAMOffset
) + SECTOR_SIZE
- 1) / SECTOR_SIZE
;
444 offset
= xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
445 ctl
= cpu_to_le32(BLOCK_CONTROL
);
447 for (i
= 0; i
< nbam
; i
++, offset
+= sizeof(u_int32_t
)) {
449 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, sizeof(u_int32_t
),
450 &retlen
, (u_char
*)&ctl
);
455 xfer
->state
= XFER_PREPARED
;
460 /*======================================================================
462 Copy_erase_unit() takes a full erase block and a transfer unit,
463 copies everything to the transfer unit, then swaps the block
466 All data blocks are copied to the corresponding blocks in the
467 target unit, so the virtual block map does not need to be
470 ======================================================================*/
472 static int copy_erase_unit(partition_t
*part
, u_int16_t srcunit
,
475 u_char buf
[SECTOR_SIZE
];
476 struct eun_info_t
*eun
;
477 struct xfer_info_t
*xfer
;
478 u_int32_t src
, dest
, free
, i
;
483 u_int16_t srcunitswap
= cpu_to_le16(srcunit
);
485 eun
= &part
->EUNInfo
[srcunit
];
486 xfer
= &part
->XferInfo
[xferunit
];
487 DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
488 eun
->Offset
, xfer
->Offset
);
491 /* Read current BAM */
492 if (part
->bam_index
!= srcunit
) {
494 offset
= eun
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
496 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
,
497 part
->BlocksPerUnit
* sizeof(u_int32_t
),
498 &retlen
, (u_char
*) (part
->bam_cache
));
500 /* mark the cache bad, in case we get an error later */
501 part
->bam_index
= 0xffff;
504 printk( KERN_WARNING
"ftl: Failed to read BAM cache in copy_erase_unit()!\n");
509 /* Write the LogicalEUN for the transfer unit */
510 xfer
->state
= XFER_UNKNOWN
;
511 offset
= xfer
->Offset
+ 20; /* Bad! */
512 unit
= cpu_to_le16(0x7fff);
514 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, sizeof(u_int16_t
),
515 &retlen
, (u_char
*) &unit
);
518 printk( KERN_WARNING
"ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
522 /* Copy all data blocks from source unit to transfer unit */
523 src
= eun
->Offset
; dest
= xfer
->Offset
;
527 for (i
= 0; i
< part
->BlocksPerUnit
; i
++) {
528 switch (BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[i
]))) {
530 /* This gets updated later */
533 case BLOCK_REPLACEMENT
:
534 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, src
, SECTOR_SIZE
,
535 &retlen
, (u_char
*) buf
);
537 printk(KERN_WARNING
"ftl: Error reading old xfer unit in copy_erase_unit\n");
542 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, dest
, SECTOR_SIZE
,
543 &retlen
, (u_char
*) buf
);
545 printk(KERN_WARNING
"ftl: Error writing new xfer unit in copy_erase_unit\n");
551 /* All other blocks must be free */
552 part
->bam_cache
[i
] = cpu_to_le32(0xffffffff);
560 /* Write the BAM to the transfer unit */
561 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
562 part
->BlocksPerUnit
* sizeof(int32_t), &retlen
,
563 (u_char
*)part
->bam_cache
);
565 printk( KERN_WARNING
"ftl: Error writing BAM in copy_erase_unit\n");
570 /* All clear? Then update the LogicalEUN again */
571 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, xfer
->Offset
+ 20, sizeof(u_int16_t
),
572 &retlen
, (u_char
*)&srcunitswap
);
575 printk(KERN_WARNING
"ftl: Error writing new LogicalEUN in copy_erase_unit\n");
580 /* Update the maps and usage stats*/
581 i
= xfer
->EraseCount
;
582 xfer
->EraseCount
= eun
->EraseCount
;
585 xfer
->Offset
= eun
->Offset
;
587 part
->FreeTotal
-= eun
->Free
;
588 part
->FreeTotal
+= free
;
592 /* Now, the cache should be valid for the new block */
593 part
->bam_index
= srcunit
;
596 } /* copy_erase_unit */
598 /*======================================================================
600 reclaim_block() picks a full erase unit and a transfer unit and
601 then calls copy_erase_unit() to copy one to the other. Then, it
602 schedules an erase on the expired block.
604 What's a good way to decide which transfer unit and which erase
605 unit to use? Beats me. My way is to always pick the transfer
606 unit with the fewest erases, and usually pick the data unit with
607 the most deleted blocks. But with a small probability, pick the
608 oldest data unit instead. This means that we generally postpone
609 the next reclaimation as long as possible, but shuffle static
610 stuff around a bit for wear leveling.
612 ======================================================================*/
614 static int reclaim_block(partition_t
*part
)
616 u_int16_t i
, eun
, xfer
;
620 DEBUG(0, "ftl_cs: reclaiming space...\n");
621 DEBUG(3, "NumTransferUnits == %x\n", part
->header
.NumTransferUnits
);
622 /* Pick the least erased transfer unit */
623 best
= 0xffffffff; xfer
= 0xffff;
626 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++) {
628 if (part
->XferInfo
[i
].state
== XFER_UNKNOWN
) {
629 DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i
);
633 if (part
->XferInfo
[i
].state
== XFER_ERASING
) {
634 DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i
);
638 else if (part
->XferInfo
[i
].state
== XFER_ERASED
) {
639 DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i
);
641 prepare_xfer(part
, i
);
643 if (part
->XferInfo
[i
].state
== XFER_PREPARED
) {
644 DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i
);
646 if (part
->XferInfo
[i
].EraseCount
<= best
) {
647 best
= part
->XferInfo
[i
].EraseCount
;
652 DEBUG(3,"XferInfo[%d].state == %x\n",i
, part
->XferInfo
[i
].state
);
655 if (xfer
== 0xffff) {
657 DEBUG(1, "ftl_cs: waiting for transfer "
658 "unit to be prepared...\n");
659 if (part
->mbd
.mtd
->sync
)
660 part
->mbd
.mtd
->sync(part
->mbd
.mtd
);
664 printk(KERN_NOTICE
"ftl_cs: reclaim failed: no "
665 "suitable transfer units!\n");
667 DEBUG(1, "ftl_cs: reclaim failed: no "
668 "suitable transfer units!\n");
673 } while (xfer
== 0xffff);
676 if ((jiffies
% shuffle_freq
) == 0) {
677 DEBUG(1, "ftl_cs: recycling freshest block...\n");
679 for (i
= 0; i
< part
->DataUnits
; i
++)
680 if (part
->EUNInfo
[i
].EraseCount
<= best
) {
681 best
= part
->EUNInfo
[i
].EraseCount
;
686 for (i
= 0; i
< part
->DataUnits
; i
++)
687 if (part
->EUNInfo
[i
].Deleted
>= best
) {
688 best
= part
->EUNInfo
[i
].Deleted
;
694 printk(KERN_NOTICE
"ftl_cs: reclaim failed: "
695 "no free blocks!\n");
697 DEBUG(1,"ftl_cs: reclaim failed: "
698 "no free blocks!\n");
703 ret
= copy_erase_unit(part
, eun
, xfer
);
705 erase_xfer(part
, xfer
);
707 printk(KERN_NOTICE
"ftl_cs: copy_erase_unit failed!\n");
709 } /* reclaim_block */
711 /*======================================================================
713 Find_free() searches for a free block. If necessary, it updates
714 the BAM cache for the erase unit containing the free block. It
715 returns the block index -- the erase unit is just the currently
716 cached unit. If there are no free blocks, it returns 0 -- this
717 is never a valid data block because it contains the header.
719 ======================================================================*/
722 static void dump_lists(partition_t
*part
)
725 printk(KERN_DEBUG
"ftl_cs: Free total = %d\n", part
->FreeTotal
);
726 for (i
= 0; i
< part
->DataUnits
; i
++)
727 printk(KERN_DEBUG
"ftl_cs: unit %d: %d phys, %d free, "
729 part
->EUNInfo
[i
].Offset
>> part
->header
.EraseUnitSize
,
730 part
->EUNInfo
[i
].Free
, part
->EUNInfo
[i
].Deleted
);
734 static u_int32_t
find_free(partition_t
*part
)
741 /* Find an erase unit with some free space */
742 stop
= (part
->bam_index
== 0xffff) ? 0 : part
->bam_index
;
745 if (part
->EUNInfo
[eun
].Free
!= 0) break;
746 /* Wrap around at end of table */
747 if (++eun
== part
->DataUnits
) eun
= 0;
748 } while (eun
!= stop
);
750 if (part
->EUNInfo
[eun
].Free
== 0)
753 /* Is this unit's BAM cached? */
754 if (eun
!= part
->bam_index
) {
755 /* Invalidate cache */
756 part
->bam_index
= 0xffff;
758 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
,
759 part
->EUNInfo
[eun
].Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
760 part
->BlocksPerUnit
* sizeof(u_int32_t
),
761 &retlen
, (u_char
*) (part
->bam_cache
));
764 printk(KERN_WARNING
"ftl: Error reading BAM in find_free\n");
767 part
->bam_index
= eun
;
770 /* Find a free block */
771 for (blk
= 0; blk
< part
->BlocksPerUnit
; blk
++)
772 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[blk
]))) break;
773 if (blk
== part
->BlocksPerUnit
) {
779 printk(KERN_NOTICE
"ftl_cs: bad free list!\n");
782 DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk
, eun
);
788 /*======================================================================
790 Read a series of sectors from an FTL partition.
792 ======================================================================*/
794 static int ftl_read(partition_t
*part
, caddr_t buffer
,
795 u_long sector
, u_long nblocks
)
797 u_int32_t log_addr
, bsize
;
800 size_t offset
, retlen
;
802 DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
803 part
, sector
, nblocks
);
804 if (!(part
->state
& FTL_FORMATTED
)) {
805 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
808 bsize
= 1 << part
->header
.EraseUnitSize
;
810 for (i
= 0; i
< nblocks
; i
++) {
811 if (((sector
+i
) * SECTOR_SIZE
) >= le32_to_cpu(part
->header
.FormattedSize
)) {
812 printk(KERN_NOTICE
"ftl_cs: bad read offset\n");
815 log_addr
= part
->VirtualBlockMap
[sector
+i
];
816 if (log_addr
== 0xffffffff)
817 memset(buffer
, 0, SECTOR_SIZE
);
819 offset
= (part
->EUNInfo
[log_addr
/ bsize
].Offset
820 + (log_addr
% bsize
));
821 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, SECTOR_SIZE
,
822 &retlen
, (u_char
*) buffer
);
825 printk(KERN_WARNING
"Error reading MTD device in ftl_read()\n");
829 buffer
+= SECTOR_SIZE
;
834 /*======================================================================
836 Write a series of sectors to an FTL partition
838 ======================================================================*/
840 static int set_bam_entry(partition_t
*part
, u_int32_t log_addr
,
843 u_int32_t bsize
, blk
, le_virt_addr
;
849 size_t retlen
, offset
;
851 DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
852 part
, log_addr
, virt_addr
);
853 bsize
= 1 << part
->header
.EraseUnitSize
;
854 eun
= log_addr
/ bsize
;
855 blk
= (log_addr
% bsize
) / SECTOR_SIZE
;
856 offset
= (part
->EUNInfo
[eun
].Offset
+ blk
* sizeof(u_int32_t
) +
857 le32_to_cpu(part
->header
.BAMOffset
));
860 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, sizeof(u_int32_t
),
861 &retlen
, (u_char
*)&old_addr
);
863 printk(KERN_WARNING
"ftl: Error reading old_addr in set_bam_entry: %d\n",ret
);
866 old_addr
= le32_to_cpu(old_addr
);
868 if (((virt_addr
== 0xfffffffe) && !BLOCK_FREE(old_addr
)) ||
869 ((virt_addr
== 0) && (BLOCK_TYPE(old_addr
) != BLOCK_DATA
)) ||
870 (!BLOCK_DELETED(virt_addr
) && (old_addr
!= 0xfffffffe))) {
873 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() inconsistency!\n");
874 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, old = 0x%x"
875 ", new = 0x%x\n", log_addr
, old_addr
, virt_addr
);
880 le_virt_addr
= cpu_to_le32(virt_addr
);
881 if (part
->bam_index
== eun
) {
883 if (le32_to_cpu(part
->bam_cache
[blk
]) != old_addr
) {
886 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() "
888 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, cache"
890 le32_to_cpu(part
->bam_cache
[blk
]), old_addr
);
895 part
->bam_cache
[blk
] = le_virt_addr
;
897 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, sizeof(u_int32_t
),
898 &retlen
, (u_char
*)&le_virt_addr
);
901 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() failed!\n");
902 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, new = 0x%x\n",
903 log_addr
, virt_addr
);
906 } /* set_bam_entry */
908 static int ftl_write(partition_t
*part
, caddr_t buffer
,
909 u_long sector
, u_long nblocks
)
911 u_int32_t bsize
, log_addr
, virt_addr
, old_addr
, blk
;
914 size_t retlen
, offset
;
916 DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
917 part
, sector
, nblocks
);
918 if (!(part
->state
& FTL_FORMATTED
)) {
919 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
922 /* See if we need to reclaim space, before we start */
923 while (part
->FreeTotal
< nblocks
) {
924 ret
= reclaim_block(part
);
929 bsize
= 1 << part
->header
.EraseUnitSize
;
931 virt_addr
= sector
* SECTOR_SIZE
| BLOCK_DATA
;
932 for (i
= 0; i
< nblocks
; i
++) {
933 if (virt_addr
>= le32_to_cpu(part
->header
.FormattedSize
)) {
934 printk(KERN_NOTICE
"ftl_cs: bad write offset\n");
938 /* Grab a free block */
939 blk
= find_free(part
);
943 printk(KERN_NOTICE
"ftl_cs: internal error: "
944 "no free blocks!\n");
948 /* Tag the BAM entry, and write the new block */
949 log_addr
= part
->bam_index
* bsize
+ blk
* SECTOR_SIZE
;
950 part
->EUNInfo
[part
->bam_index
].Free
--;
952 if (set_bam_entry(part
, log_addr
, 0xfffffffe))
954 part
->EUNInfo
[part
->bam_index
].Deleted
++;
955 offset
= (part
->EUNInfo
[part
->bam_index
].Offset
+
957 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, SECTOR_SIZE
, &retlen
,
961 printk(KERN_NOTICE
"ftl_cs: block write failed!\n");
962 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, virt_addr"
963 " = 0x%x, Offset = 0x%zx\n", log_addr
, virt_addr
,
968 /* Only delete the old entry when the new entry is ready */
969 old_addr
= part
->VirtualBlockMap
[sector
+i
];
970 if (old_addr
!= 0xffffffff) {
971 part
->VirtualBlockMap
[sector
+i
] = 0xffffffff;
972 part
->EUNInfo
[old_addr
/bsize
].Deleted
++;
973 if (set_bam_entry(part
, old_addr
, 0))
977 /* Finally, set up the new pointers */
978 if (set_bam_entry(part
, log_addr
, virt_addr
))
980 part
->VirtualBlockMap
[sector
+i
] = log_addr
;
981 part
->EUNInfo
[part
->bam_index
].Deleted
--;
983 buffer
+= SECTOR_SIZE
;
984 virt_addr
+= SECTOR_SIZE
;
989 static int ftl_getgeo(struct mtd_blktrans_dev
*dev
, struct hd_geometry
*geo
)
991 partition_t
*part
= (void *)dev
;
994 /* Sort of arbitrary: round size down to 4KiB boundary */
995 sect
= le32_to_cpu(part
->header
.FormattedSize
)/SECTOR_SIZE
;
999 geo
->cylinders
= sect
>> 3;
1004 static int ftl_readsect(struct mtd_blktrans_dev
*dev
,
1005 unsigned long block
, char *buf
)
1007 return ftl_read((void *)dev
, buf
, block
, 1);
1010 static int ftl_writesect(struct mtd_blktrans_dev
*dev
,
1011 unsigned long block
, char *buf
)
1013 return ftl_write((void *)dev
, buf
, block
, 1);
1016 /*====================================================================*/
1018 void ftl_freepart(partition_t
*part
)
1020 vfree(part
->VirtualBlockMap
);
1021 part
->VirtualBlockMap
= NULL
;
1022 kfree(part
->VirtualPageMap
);
1023 part
->VirtualPageMap
= NULL
;
1024 kfree(part
->EUNInfo
);
1025 part
->EUNInfo
= NULL
;
1026 kfree(part
->XferInfo
);
1027 part
->XferInfo
= NULL
;
1028 kfree(part
->bam_cache
);
1029 part
->bam_cache
= NULL
;
1030 } /* ftl_freepart */
1032 static void ftl_add_mtd(struct mtd_blktrans_ops
*tr
, struct mtd_info
*mtd
)
1034 partition_t
*partition
;
1036 partition
= kzalloc(sizeof(partition_t
), GFP_KERNEL
);
1039 printk(KERN_WARNING
"No memory to scan for FTL on %s\n",
1044 partition
->mbd
.mtd
= mtd
;
1046 if ((scan_header(partition
) == 0) &&
1047 (build_maps(partition
) == 0)) {
1049 partition
->state
= FTL_FORMATTED
;
1051 printk(KERN_INFO
"ftl_cs: opening %d KiB FTL partition\n",
1052 le32_to_cpu(partition
->header
.FormattedSize
) >> 10);
1054 partition
->mbd
.size
= le32_to_cpu(partition
->header
.FormattedSize
) >> 9;
1056 partition
->mbd
.tr
= tr
;
1057 partition
->mbd
.devnum
= -1;
1058 if (!add_mtd_blktrans_dev((void *)partition
))
1062 ftl_freepart(partition
);
1066 static void ftl_remove_dev(struct mtd_blktrans_dev
*dev
)
1068 del_mtd_blktrans_dev(dev
);
1069 ftl_freepart((partition_t
*)dev
);
1073 struct mtd_blktrans_ops ftl_tr
= {
1076 .part_bits
= PART_BITS
,
1077 .blksize
= SECTOR_SIZE
,
1078 .readsect
= ftl_readsect
,
1079 .writesect
= ftl_writesect
,
1080 .getgeo
= ftl_getgeo
,
1081 .add_mtd
= ftl_add_mtd
,
1082 .remove_dev
= ftl_remove_dev
,
1083 .owner
= THIS_MODULE
,
1086 static int init_ftl(void)
1088 DEBUG(0, "$Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $\n");
1090 return register_mtd_blktrans(&ftl_tr
);
1093 static void __exit
cleanup_ftl(void)
1095 deregister_mtd_blktrans(&ftl_tr
);
1098 module_init(init_ftl
);
1099 module_exit(cleanup_ftl
);
1102 MODULE_LICENSE("Dual MPL/GPL");
1103 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1104 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");